pax_global_header00006660000000000000000000000064146363503770014530gustar00rootroot0000000000000052 comment=5e210961d97fb0504d6c5f5ab2525b13de5850ee prometheus-squid-exporter-1.12.0/000077500000000000000000000000001463635037700167755ustar00rootroot00000000000000prometheus-squid-exporter-1.12.0/.dockerignore000066400000000000000000000001021463635037700214420ustar00rootroot00000000000000.git .github dashboards helm prometheus vendor Makefile Dockerfileprometheus-squid-exporter-1.12.0/.github/000077500000000000000000000000001463635037700203355ustar00rootroot00000000000000prometheus-squid-exporter-1.12.0/.github/FUNDING.yml000066400000000000000000000012761463635037700221600ustar00rootroot00000000000000# These are supported funding model platforms github: [boynux] patreon: # Replace with a single Patreon username open_collective: # Replace with a single Open Collective username ko_fi: boynux tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] prometheus-squid-exporter-1.12.0/.github/ISSUE_TEMPLATE/000077500000000000000000000000001463635037700225205ustar00rootroot00000000000000prometheus-squid-exporter-1.12.0/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000007031463635037700252120ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: **Expected behavior** A clear and concise description of what you expected to happen. **OS (please complete the following information):** - OS: [e.g. Ubuntu] - Version [e.g. v0.4] **Additional context** Add any other context about the problem here. prometheus-squid-exporter-1.12.0/.github/workflows/000077500000000000000000000000001463635037700223725ustar00rootroot00000000000000prometheus-squid-exporter-1.12.0/.github/workflows/docker.yml000066400000000000000000000016551463635037700243730ustar00rootroot00000000000000name: Build multi-arch docker images on: push: branches: [master] tags: ['v*'] jobs: build: runs-on: ubuntu-latest name: Build and publish steps: - name: Checkout uses: actions/checkout@v4 - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Login to DockerHub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Docker meta id: meta uses: docker/metadata-action@v5 with: images: boynux/squid-exporter - name: Build and push uses: docker/build-push-action@v5 with: tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} platforms: linux/amd64,linux/arm64,linux/arm/v7 pull: true push: true prometheus-squid-exporter-1.12.0/.github/workflows/release.yml000066400000000000000000000037711463635037700245450ustar00rootroot00000000000000name: Makefile CI on: push: tags: - "v*" branches: - "master" jobs: build: runs-on: ubuntu-latest strategy: fail-fast: false matrix: arch: [386, amd64, arm64] include: - arch: arm armver: 6 - arch: arm armver: 7 env: arch_name: ${{ matrix.arch }}${{ matrix.arch == 'arm' && 'v' || '' }}${{ matrix.armver }} name: Build for ${{ matrix.arch }}${{ matrix.arch == 'arm' && 'v' || '' }}${{ matrix.armver }} steps: - name: Checkout repository uses: actions/checkout@v4 - name: Setup Go environment uses: actions/setup-go@v4 with: # Path to the go.mod or go.work file. go-version-file: go.mod - name: Build ${{ env.arch_name }} binary run: make build env: GOARCH: ${{ matrix.arch }} GOARM: ${{ matrix.armver }} - name: Rename ${{ env.arch_name }} binary run: mv bin/squid-exporter bin/squid-exporter-linux-${{ env.arch_name }} - name: Upload binary uses: actions/upload-artifact@v3 with: name: binary path: bin/* release: runs-on: ubuntu-latest steps: - name: Download binaries for release uses: actions/download-artifact@v3 with: name: binary path: bin - name: Release latest version uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} file: bin/squid-exporter-* file_glob: true tag: unstable overwrite: true body: "pre-release" prerelease: true - name: Release tag version if: startsWith(github.ref, 'refs/tags/v') uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} file: bin/squid-exporter-* file_glob: true tag: ${{ github.ref }} overwrite: true body: "Stable release" needs: [build] prometheus-squid-exporter-1.12.0/.gitignore000066400000000000000000000000331463635037700207610ustar00rootroot00000000000000bin/* squid-exporter *.swp prometheus-squid-exporter-1.12.0/.gitpod.yml000066400000000000000000000001271463635037700210640ustar00rootroot00000000000000tasks: - init: go get && go build ./... && go test ./... && make command: go run prometheus-squid-exporter-1.12.0/.travis.yml000066400000000000000000000004161463635037700211070ustar00rootroot00000000000000language: go services: - docker arch: - amd64 os: linux dist: xenial go: - 1.16.x deploy: - provider: script on: tags: true script: bash ./deploy.sh - provider: releases skip_cleanup: true token: $GH_TOKEN file: bin/squid-exporter on: tags: true prometheus-squid-exporter-1.12.0/CODE_OF_CONDUCT.md000066400000000000000000000062151463635037700216000ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at boynux@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ prometheus-squid-exporter-1.12.0/Dockerfile000066400000000000000000000015171463635037700207730ustar00rootroot00000000000000FROM golang:1.20-alpine as build ARG TARGETPLATFORM RUN echo "Building for ${TARGETPLATFORM}" WORKDIR /go/src COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 go build -a -ldflags '-extldflags "-s -w -static"' -o /squid-exporter . FROM gcr.io/distroless/static:nonroot as final LABEL org.opencontainers.image.title="Squid Exporter" LABEL org.opencontainers.image.description="This is a Docker image for Squid Prometheus Exporter." LABEL org.opencontainers.image.source="https://github.com/boynux/squid-exporter/" LABEL org.opencontainers.image.licenses="MIT" ENV SQUID_EXPORTER_LISTEN="0.0.0.0:9301" COPY --from=build /squid-exporter /usr/local/bin/squid-exporter # Allow /etc/hosts to be used for DNS COPY --from=build /etc/nsswitch.conf /etc/nsswitch.conf EXPOSE 9301 ENTRYPOINT ["/usr/local/bin/squid-exporter"] prometheus-squid-exporter-1.12.0/LICENSE000066400000000000000000000020561463635037700200050ustar00rootroot00000000000000MIT License Copyright (c) 2017 Mohammad Arab Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. prometheus-squid-exporter-1.12.0/Makefile000066400000000000000000000013301463635037700204320ustar00rootroot00000000000000.PHONY: all fmt vet test build docker clean all: fmt vet test build EXE = ./bin/squid-exporter SRC = $(shell find . -type f -name '*.go') VERSION ?= $(shell cat VERSION) REVISION = $(shell git rev-parse HEAD) BRANCH = $(shell git rev-parse --abbrev-ref HEAD) LDFLAGS = -extldflags "-s -w -static" \ -X github.com/prometheus/common/version.Version=$(VERSION) \ -X github.com/prometheus/common/version.Revision=$(REVISION) \ -X github.com/prometheus/common/version.Branch=$(BRANCH) $(EXE): $(SRC) CGO_ENABLED=0 GOOS=linux go build -a -ldflags '$(LDFLAGS)' -o $(EXE) . fmt: go fmt ./... vet: go vet ./... test: go test -v ./... build: $(EXE) docker: docker build -t squid-exporter . clean: rm -f $(EXE) prometheus-squid-exporter-1.12.0/README.md000066400000000000000000000126101463635037700202540ustar00rootroot00000000000000[![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/boynux/squid-exporter) [![Github Actions](https://github.com/boynux/squid-exporter/actions/workflows/release.yml/badge.svg)](https://github.com/boynux/squid-exporter/actions/workflows/release.yml) [![Github Docker](https://github.com/boynux/squid-exporter/actions/workflows/docker.yml/badge.svg)](https://github.com/boynux/squid-exporter/actions/workflows/docker.yml) [![Go Report Card](https://goreportcard.com/badge/github.com/boynux/squid-exporter)](https://goreportcard.com/report/github.com/boynux/squid-exporter) [![Maintainability](https://api.codeclimate.com/v1/badges/a99a88d28ad37a79dbf6/maintainability)](https://codeclimate.com/github/boynux/squid-exporter) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=3TH7YAMMEC5L4&source=url) **Note: I've been very busy on the past couple of months with my personal life and work. Thanks for filing issues and feature requests. I'll start to go through them and provide updates very soon.** Squid Prometheus exporter -------------------------- Exports squid metrics in Prometheus format **NOTE**: From release 1.0 metric names and some parameters has changed. Make sure you check the docs and update your deployments accordingly! New ----- * Using environment variables to configure the exporter * Adding custom labels to metrics * Enabling TLS for exporter via [WebConfig](https://github.com/prometheus/exporter-toolkit/blob/master/docs/web-configuration.md) Usage: ------ Simple usage: squid-exporter -squid-hostname "localhost" -squid-port 3128 [Configure Prometheus](https://github.com/boynux/squid-exporter/blob/master/prometheus/prometheus.yml) to scrape metrics from `localhost:9301/metrics` - job_name: squid # squid-exporter is installed, grab stats about the local # squid instance. target_groups: - targets: ['localhost:9301'] To get all the parameteres, command line arguments always override default and environment variables configs: squid-exporter -help The following environment variables can be used to override default parameters: ``` SQUID_EXPORTER_LISTEN SQUID_EXPORTER_WEB_CONFIG_PATH SQUID_EXPORTER_METRICS_PATH SQUID_HOSTNAME SQUID_PORT SQUID_LOGIN SQUID_PASSWORD SQUID_EXTRACTSERVICETIMES ``` Usage with docker: ------ Basic setup assuming Squid is running on the same machine: docker run --net=host -d boynux/squid-exporter Setup with Squid running on a different host docker run -p 9301:9301 -d boynux/squid-exporter -squid-hostname "192.168.0.2" -squid-port 3128 -listen ":9301" With environment variables docker run -p 9301:9301 -d -e SQUID_PORT="3128" -e SQUID_HOSTNAME="192.168.0.2" -e SQUID_EXPORTER_LISTEN=":9301" boynux/squid-exporter Build: -------- This project is written in Go, so all the usual methods for building (or cross compiling) a Go application would work. If you are not very familiar with Go you can download the binary from [releases](https://github.com/boynux/squid-exporter/releases). Or build it for your OS: `go install https://github.com/boynux/squid-exporter` then you can find the binary in: `$GOPATH/bin/squid-exporter` Features: --------- - [ ] Expose Squid counters - [x] Client HTTP - [x] Server HTTP - [x] Server ALL - [x] Server FTP - [x] Server Other - [ ] ICP - [ ] CD - [x] Swap - [ ] Page Faults - [ ] Others - [ ] Expose Squid service times - [x] HTTP requests - [x] Cache misses - [x] Cache hits - [x] Near hits - [ ] Not-Modified replies - [x] DNS lookups - [ ] ICP queries - [ ] Expose squid Info - [x] Squid service info (as label) - [x] Connection information for squid - [x] Cache information for squid - [ ] Median Service Times (seconds) 5 min - [x] Resource usage for squid - [x] Memory accounted for - [x] File descriptor usage for squid - [x] Internal Data Structures - [ ] Histograms - [ ] Other metrics - [x] Squid Authentication (Basic Auth) FAQ: -------- - Q: Metrics are not reported by exporter - A: That usually means the exporter cannot reach squid server or the config manager permissions are not set corretly. To debug and mitigate: - First make sure the exporter service can reach to squid server IP Address (you can use telnet to test that) - Make sure you allow exporter to query the squid server in config you will need something like this (`172.20.0.0/16` is the network for exporter, you can also use a single IP if needed): ``` #http_access allow manager localhost acl prometheus src 172.20.0.0/16 http_access allow manager prometheus ``` - Q: Why `process_open_fds` metric is not exported? - A: This usualy means exporter don't have permission to read `/proc//fd` folder. You can either 1. _[recommended]_ Set `CAP_DAC_READ_SEARCH` capability for squid exporter process (or docker). (eg. `sudo setcap 'cap_dac_read_search+ep' ./bin/squid-exporter`) 2. _[not recommended]_ Run the exporter as root. Contribution: ------------- Pull request and issues are very welcome. If you found this program useful please consider donations [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=3TH7YAMMEC5L4&source=url) Copyright: ---------- [MIT License](https://opensource.org/licenses/MIT) prometheus-squid-exporter-1.12.0/VERSION000066400000000000000000000000071463635037700200420ustar00rootroot000000000000001.12.0 prometheus-squid-exporter-1.12.0/_config.yml000066400000000000000000000000331463635037700211200ustar00rootroot00000000000000theme: jekyll-theme-minimalprometheus-squid-exporter-1.12.0/collector/000077500000000000000000000000001463635037700207635ustar00rootroot00000000000000prometheus-squid-exporter-1.12.0/collector/client.go000066400000000000000000000241711463635037700225750ustar00rootroot00000000000000package collector import ( "bufio" "encoding/base64" "errors" "fmt" "io" "log" "net" "net/http" "strconv" "strings" "github.com/boynux/squid-exporter/types" ) /*CacheObjectClient holds information about squid manager */ type CacheObjectClient struct { ch connectionHandler basicAuthString string headers []string } type connectionHandler interface { connect() (net.Conn, error) } type connectionHandlerImpl struct { hostname string port int } /*SquidClient provides functionality to fetch squid metrics */ type SquidClient interface { GetCounters() (types.Counters, error) GetServiceTimes() (types.Counters, error) GetInfos() (types.Counters, error) } const ( requestProtocol = "GET cache_object://localhost/%s HTTP/1.0" ) func buildBasicAuthString(login string, password string) string { if len(login) == 0 { return "" } else { return base64.StdEncoding.EncodeToString([]byte(login + ":" + password)) } } type CacheObjectRequest struct { Hostname string Port int Login string Password string Headers []string } /*NewCacheObjectClient initializes a new cache client */ func NewCacheObjectClient(cor *CacheObjectRequest) *CacheObjectClient { return &CacheObjectClient{ &connectionHandlerImpl{ cor.Hostname, cor.Port, }, buildBasicAuthString(cor.Login, cor.Password), cor.Headers, } } func (c *CacheObjectClient) readFromSquid(endpoint string) (*bufio.Reader, error) { conn, err := c.ch.connect() if err != nil { return nil, err } r, err := get(conn, endpoint, c.basicAuthString, c.headers) if err != nil { return nil, err } if r.StatusCode != 200 { return nil, fmt.Errorf("Non success code %d while fetching metrics", r.StatusCode) } return bufio.NewReader(r.Body), err } func readLines(reader *bufio.Reader, lines chan<- string) { for { line, err := reader.ReadString('\n') if err == io.EOF { break } if err != nil { log.Printf("error reading from the bufio.Reader: %v", err) break } lines <- line } close(lines) } /*GetCounters fetches counters from squid cache manager */ func (c *CacheObjectClient) GetCounters() (types.Counters, error) { var counters types.Counters reader, err := c.readFromSquid("counters") if err != nil { return nil, fmt.Errorf("error getting counters: %v", err) } lines := make(chan string) go readLines(reader, lines) for line := range lines { c, err := decodeCounterStrings(line) if err != nil { log.Println(err) } else { counters = append(counters, c) } } return counters, err } /*GetServiceTimes fetches service times from squid cache manager */ func (c *CacheObjectClient) GetServiceTimes() (types.Counters, error) { var serviceTimes types.Counters reader, err := c.readFromSquid("service_times") if err != nil { return nil, fmt.Errorf("error getting service times: %v", err) } lines := make(chan string) go readLines(reader, lines) for line := range lines { s, err := decodeServiceTimeStrings(line) if err != nil { log.Println(err) } else { if s.Key != "" { serviceTimes = append(serviceTimes, s) } } } return serviceTimes, err } /*GetInfos fetches info from squid cache manager */ func (c *CacheObjectClient) GetInfos() (types.Counters, error) { var infos types.Counters reader, err := c.readFromSquid("info") if err != nil { return nil, fmt.Errorf("error getting info: %v", err) } lines := make(chan string) go readLines(reader, lines) var infoVarLabels types.Counter infoVarLabels.Key = "squid_info" infoVarLabels.Value = 1 for line := range lines { dis, err := decodeInfoStrings(line) if err != nil { log.Println(err) } else { if len(dis.VarLabels) > 0 { if dis.VarLabels[0].Key == "5min" { var infoAvg5 types.Counter var infoAvg60 types.Counter infoAvg5.Key = dis.Key + "_" + dis.VarLabels[0].Key infoAvg60.Key = dis.Key + "_" + dis.VarLabels[1].Key if value, err := strconv.ParseFloat(dis.VarLabels[0].Value, 64); err == nil { infoAvg5.Value = value infos = append(infos, infoAvg5) } if value, err := strconv.ParseFloat(dis.VarLabels[1].Value, 64); err == nil { infoAvg60.Value = value infos = append(infos, infoAvg60) } } else { infoVarLabels.VarLabels = append(infoVarLabels.VarLabels, dis.VarLabels[0]) } } else if dis.Key != "" { infos = append(infos, dis) } } } infos = append(infos, infoVarLabels) return infos, err } func (ch *connectionHandlerImpl) connect() (net.Conn, error) { return net.Dial("tcp", fmt.Sprintf("%s:%d", ch.hostname, ch.port)) } func get(conn net.Conn, path string, basicAuthString string, headers []string) (*http.Response, error) { rBody := append(headers, []string{ fmt.Sprintf(requestProtocol, path), "Host: localhost", "User-Agent: squidclient/3.5.12", }...) if len(basicAuthString) > 0 { rBody = append(rBody, "Proxy-Authorization: Basic "+basicAuthString) rBody = append(rBody, "Authorization: Basic "+basicAuthString) } rBody = append(rBody, "Accept: */*", "\r\n") request := strings.Join(rBody, "\r\n") fmt.Fprint(conn, request) return http.ReadResponse(bufio.NewReader(conn), nil) } func decodeCounterStrings(line string) (types.Counter, error) { if equal := strings.Index(line, "="); equal >= 0 { if key := strings.TrimSpace(line[:equal]); len(key) > 0 { value := "" if len(line) > equal { value = strings.TrimSpace(line[equal+1:]) } // Remove additional formating string from `sample_time` if slices := strings.Split(value, " "); len(slices) > 0 { value = slices[0] } if i, err := strconv.ParseFloat(value, 64); err == nil { return types.Counter{Key: key, Value: i}, nil } } } return types.Counter{}, errors.New("counter - could not parse line: " + line) } func decodeServiceTimeStrings(line string) (types.Counter, error) { if strings.HasSuffix(line, ":\n") { // A header line isn't a metric return types.Counter{}, nil } if equal := strings.Index(line, ":"); equal >= 0 { if key := strings.TrimSpace(line[:equal]); len(key) > 0 { value := "" if len(line) > equal { value = strings.TrimSpace(line[equal+1:]) } key = strings.Replace(key, " ", "_", -1) key = strings.Replace(key, "(", "", -1) key = strings.Replace(key, ")", "", -1) if equalTwo := strings.Index(value, "%"); equalTwo >= 0 { if keyTwo := strings.TrimSpace(value[:equalTwo]); len(keyTwo) > 0 { if len(value) > equalTwo { value = strings.Split(strings.TrimSpace(value[equalTwo+1:]), " ")[0] } key = key + "_" + keyTwo } } if value, err := strconv.ParseFloat(value, 64); err == nil { return types.Counter{Key: key, Value: value}, nil } } } return types.Counter{}, errors.New("service times - could not parse line: " + line) } func decodeInfoStrings(line string) (types.Counter, error) { if strings.HasSuffix(line, ":\n") { // A header line isn't a metric return types.Counter{}, nil } if idx := strings.Index(line, ":"); idx >= 0 { // detect if line contain metric format like "metricName: value" if key := strings.TrimSpace(line[:idx]); len(key) > 0 { value := "" if len(line) > idx { value = strings.TrimSpace(line[idx+1:]) } key = strings.Replace(key, " ", "_", -1) key = strings.Replace(key, "(", "", -1) key = strings.Replace(key, ")", "", -1) key = strings.Replace(key, ",", "", -1) key = strings.Replace(key, "/", "", -1) // metrics with value as string need to save as label, format like "Squid Object Cache: Version 6.1" (the 3 first metrics) if key == "Squid_Object_Cache" || key == "Build_Info" || key == "Service_Name" { if key == "Squid_Object_Cache" { // To clarify that the value is the squid version. key = key + "_Version" if slices := strings.Split(value, " "); len(slices) > 0 { value = slices[1] } } var infoVarLabel types.VarLabel infoVarLabel.Key = key infoVarLabel.Value = value var infoCounter types.Counter infoCounter.Key = key infoCounter.VarLabels = append(infoCounter.VarLabels, infoVarLabel) return infoCounter, nil } else if key == "Start_Time" || key == "Current_Time" { // discart this metrics return types.Counter{}, nil } // Remove additional information in value metric if slices := strings.Split(value, " "); len(slices) > 0 { if slices[0] == "5min:" && slices[2] == "60min:" { // catch metrics with avg in 5min and 60min format like "Hits as % of bytes sent: 5min: -0.0%, 60min: -0.0%" var infoAvg5mVarLabel types.VarLabel infoAvg5mVarLabel.Key = slices[0] infoAvg5mVarLabel.Value = slices[1] infoAvg5mVarLabel.Key = strings.Replace(infoAvg5mVarLabel.Key, ":", "", -1) infoAvg5mVarLabel.Value = strings.Replace(infoAvg5mVarLabel.Value, "%", "", -1) infoAvg5mVarLabel.Value = strings.Replace(infoAvg5mVarLabel.Value, ",", "", -1) var infoAvg60mVarLabel types.VarLabel infoAvg60mVarLabel.Key = slices[2] infoAvg60mVarLabel.Value = slices[3] infoAvg60mVarLabel.Key = strings.Replace(infoAvg60mVarLabel.Key, ":", "", -1) infoAvg60mVarLabel.Value = strings.Replace(infoAvg60mVarLabel.Value, "%", "", -1) infoAvg60mVarLabel.Value = strings.Replace(infoAvg60mVarLabel.Value, ",", "", -1) var infoAvgCounter types.Counter infoAvgCounter.Key = key infoAvgCounter.VarLabels = append(infoAvgCounter.VarLabels, infoAvg5mVarLabel, infoAvg60mVarLabel) return infoAvgCounter, nil } else { value = slices[0] } } value = strings.Replace(value, "%", "", -1) value = strings.Replace(value, ",", "", -1) if i, err := strconv.ParseFloat(value, 64); err == nil { return types.Counter{Key: key, Value: i}, nil } } } else { // this catch the last 4 metrics format like "value metricName" lineTrimed := strings.TrimSpace(line[:]) if idx := strings.Index(lineTrimed, " "); idx >= 0 { key := strings.TrimSpace(lineTrimed[idx+1:]) key = strings.Replace(key, " ", "_", -1) key = strings.Replace(key, "-", "_", -1) value := strings.TrimSpace(lineTrimed[:idx]) if i, err := strconv.ParseFloat(value, 64); err == nil { return types.Counter{Key: key, Value: i}, nil } } } return types.Counter{}, errors.New("Info - could not parse line: " + line) } prometheus-squid-exporter-1.12.0/collector/client_test.go000066400000000000000000000042051463635037700236300ustar00rootroot00000000000000package collector import ( "net" "testing" "github.com/boynux/squid-exporter/types" "github.com/stretchr/testify/assert" ) type mockConnectionHandler struct { server net.Conn buffer []byte } func (c *mockConnectionHandler) connect() (net.Conn, error) { var client net.Conn c.server, client = net.Pipe() return client, nil } func TestBuildBasicAuth(t *testing.T) { u := "test_username" p := "test_password" expectedAuthString := "dGVzdF91c2VybmFtZTp0ZXN0X3Bhc3N3b3Jk" ba := buildBasicAuthString(u, p) assert.Equal(t, expectedAuthString, ba, "Basic Auth format doesn't match") } func TestReadFromSquid(t *testing.T) { ch := &mockConnectionHandler{} go func() { b := make([]byte, 256) n, _ := ch.server.Read(b) ch.buffer = append(ch.buffer, b[:n]...) ch.server.Write(b[n:]) ch.server.Close() }() coc := &CacheObjectClient{ ch, "", []string{}, } expected := "GET cache_object://localhost/test HTTP/1.0\r\nHost: localhost\r\nUser-Agent: squidclient/3.5.12\r\nAccept: */*\r\n\r\n" coc.readFromSquid("test") assert.Equal(t, expected, string(ch.buffer)) } func TestDecodeMetricStrings(t *testing.T) { tests := []struct { s string c types.Counter e string d func(string) (types.Counter, error) }{ {"swap.files_cleaned=1", types.Counter{Key: "swap.files_cleaned", Value: 1}, "", decodeCounterStrings}, {"client.http_requests=1", types.Counter{Key: "client.http_requests", Value: 1}, "", decodeCounterStrings}, {"# test for invalid metric line", types.Counter{}, "counter - could not parse line: # test for invalid metric line", decodeCounterStrings}, {" HTTP Requests (All): 70% 10.00000 9.50000\n", types.Counter{Key: "HTTP_Requests_All_70", Value: 10}, "", decodeServiceTimeStrings}, {" Not-Modified Replies: 5% 12.00000 10.00000\n", types.Counter{Key: "Not-Modified_Replies_5", Value: 12}, "", decodeServiceTimeStrings}, {" ICP Queries: 85% 900.00000 1200.00000\n", types.Counter{Key: "ICP_Queries_85", Value: 900}, "", decodeServiceTimeStrings}, } for _, tc := range tests { c, err := tc.d(tc.s) if tc.e != "" { assert.EqualError(t, err, tc.e) } assert.Equal(t, tc.c, c) } } prometheus-squid-exporter-1.12.0/collector/counters.go000066400000000000000000000055241463635037700231620ustar00rootroot00000000000000package collector import ( "fmt" "strings" "github.com/prometheus/client_golang/prometheus" ) type squidCounter struct { Section string Counter string Suffix string Description string } var squidCounters = []squidCounter{ {"client_http", "requests", "total", "The total number of client requests"}, {"client_http", "hits", "total", "The total number of client cache hits"}, {"client_http", "errors", "total", "The total number of client http errors"}, {"client_http", "kbytes_in", "kbytes_total", "The total number of client kbytes received"}, {"client_http", "kbytes_out", "kbytes_total", "The total number of client kbytes transferred"}, {"client_http", "hit_kbytes_out", "bytes_total", "The total number of client kbytes cache hit"}, {"server.http", "requests", "total", "The total number of server http requests"}, {"server.http", "errors", "total", "The total number of server http errors"}, {"server.http", "kbytes_in", "kbytes_total", "The total number of server http kbytes received"}, {"server.http", "kbytes_out", "kbytes_total", "The total number of server http kbytes transferred"}, {"server.all", "requests", "total", "The total number of server all requests"}, {"server.all", "errors", "total", "The total number of server all errors"}, {"server.all", "kbytes_in", "kbytes_total", "The total number of server kbytes received"}, {"server.all", "kbytes_out", "kbytes_total", "The total number of server kbytes transferred"}, {"server.ftp", "requests", "total", "The total number of server ftp requests"}, {"server.ftp", "errors", "total", "The total number of server ftp errors"}, {"server.ftp", "kbytes_in", "kbytes_total", "The total number of server ftp kbytes received"}, {"server.ftp", "kbytes_out", "kbytes_total", "The total number of server ftp kbytes transferred"}, {"server.other", "requests", "total", "The total number of server other requests"}, {"server.other", "errors", "total", "The total number of server other errors"}, {"server.other", "kbytes_in", "kbytes_total", "The total number of server other kbytes received"}, {"server.other", "kbytes_out", "kbytes_total", "The total number of server other kbytes transferred"}, {"swap", "ins", "total", "The number of objects read from disk"}, {"swap", "outs", "total", "The number of objects saved to disk"}, {"swap", "files_cleaned", "total", "The number of orphaned cache files removed by the periodic cleanup procedure"}, } func generateSquidCounters(labels []string) descMap { counters := descMap{} for i := range squidCounters { counter := squidCounters[i] counters[fmt.Sprintf("%s.%s", counter.Section, counter.Counter)] = prometheus.NewDesc( prometheus.BuildFQName(namespace, strings.Replace(counter.Section, ".", "_", -1), fmt.Sprintf("%s_%s", counter.Counter, counter.Suffix)), counter.Description, labels, nil, ) } return counters } prometheus-squid-exporter-1.12.0/collector/infos.go000066400000000000000000000055541463635037700224410ustar00rootroot00000000000000package collector import ( "strings" "github.com/prometheus/client_golang/prometheus" ) type squidInfos struct { Section string Description string Unit string } var squidInfoss = []squidInfos{ {"Number_of_clients_accessing_cache", "", "number"}, {"Number_of_HTTP_requests_received", "", "number"}, {"Number_of_ICP_messages_received", "", "number"}, {"Number_of_ICP_messages_sent", "", "number"}, {"Number_of_queued_ICP_replies", "", "number"}, {"Number_of_HTCP_messages_received", "", "number"}, {"Number_of_HTCP_messages_sent", "", "number"}, {"Request_failure_ratio", "", "%"}, {"Average_HTTP_requests_per_minute_since_start", "", "%"}, {"Average_ICP_messages_per_minute_since_start", "", "%"}, {"Select_loop_called", "", "number"}, {"Hits_as_%_of_all_requests_5min", "", "%"}, {"Hits_as_%_of_bytes_sent_5min", "", "%"}, {"Memory_hits_as_%_of_hit_requests_5min", "", "%"}, {"Disk_hits_as_%_of_hit_requests_5min", "", "%"}, {"Hits_as_%_of_all_requests_60min", "", "%"}, {"Hits_as_%_of_bytes_sent_60min", "", "%"}, {"Memory_hits_as_%_of_hit_requests_60min", "", "%"}, {"Disk_hits_as_%_of_hit_requests_60min", "", "%"}, {"Storage_Swap_size", "", "KB"}, {"Storage_Swap_capacity", "", "% use"}, {"Storage_Mem_size", "", "KB"}, {"Storage_Mem_capacity", "", "% used"}, {"Mean_Object_Size", "", "KB"}, {"Requests_given_to_unlinkd", "", "number"}, {"UP_Time", "time squid is up", "seconds"}, {"CPU_Time", "", "seconds"}, {"CPU_Usage", "of cpu usage", "%"}, {"CPU_Usage_5_minute_avg", "of cpu usage", "%"}, {"CPU_Usage_60_minute_avg", "of cpu usage", "%"}, {"Maximum_Resident_Size", "", "KB"}, {"Page_faults_with_physical_i_o", "", "number"}, {"Total_accounted", "", "KB"}, {"memPoolAlloc_calls", "", "number"}, {"memPoolFree_calls", "", "number"}, {"Maximum_number_of_file_descriptors", "", "number"}, {"Largest_file_desc_currently_in_use", "", "number"}, {"Number_of_file_desc_currently_in_use", "", "number"}, {"Files_queued_for_open", "", "number"}, {"Available_number_of_file_descriptors", "", "number"}, {"Reserved_number_of_file_descriptors", "", "number"}, {"Store_Disk_files_open", "", "number"}, {"StoreEntries", "", "number"}, {"StoreEntries_with_MemObjects", "", "number"}, {"Hot_Object_Cache_Items", "", "number"}, {"on_disk_objects", "", "number"}, } func generateSquidInfos(labels []string) descMap { infos := descMap{} for i := range squidInfoss { info := squidInfoss[i] var key string var name string var description string key = info.Section name = prometheus.BuildFQName(namespace, "info", strings.Replace(info.Section, "%", "pct", -1)) if info.Description == "" { description = strings.Replace(info.Section, "_", " ", -1) } else { description = info.Description } description = description + " in " + info.Unit infos[key] = prometheus.NewDesc( name, description, labels, nil, ) } return infos } prometheus-squid-exporter-1.12.0/collector/metrics.go000066400000000000000000000070321463635037700227620ustar00rootroot00000000000000package collector import ( "log" "time" "github.com/boynux/squid-exporter/config" "github.com/prometheus/client_golang/prometheus" ) type descMap map[string]*prometheus.Desc const ( namespace = "squid" timeout = 10 * time.Second ) var ( counters descMap serviceTimes descMap // ExtractServiceTimes decides if we want to extract service times ExtractServiceTimes bool infos descMap ) /*Exporter entry point to squid exporter */ type Exporter struct { client SquidClient hostname string port int labels config.Labels up *prometheus.GaugeVec } type CollectorConfig struct { Hostname string Port int Login string Password string Labels config.Labels Headers []string } /*New initializes a new exporter */ func New(c *CollectorConfig) *Exporter { counters = generateSquidCounters(c.Labels.Keys) if ExtractServiceTimes { serviceTimes = generateSquidServiceTimes(c.Labels.Keys) } infos = generateSquidInfos(c.Labels.Keys) return &Exporter{ NewCacheObjectClient(&CacheObjectRequest{ c.Hostname, c.Port, c.Login, c.Password, c.Headers, }), c.Hostname, c.Port, c.Labels, prometheus.NewGaugeVec(prometheus.GaugeOpts{ Namespace: namespace, Name: "up", Help: "Was the last query of squid successful?", }, []string{"host"}), } } // Describe describes all the metrics ever exported by the ECS exporter. It // implements prometheus.Collector. func (e *Exporter) Describe(ch chan<- *prometheus.Desc) { e.up.Describe(ch) for _, v := range counters { ch <- v } if ExtractServiceTimes { for _, v := range serviceTimes { ch <- v } } for _, v := range infos { ch <- v } } /*Collect fetches metrics from squid manager and pushes them to promethus */ func (e *Exporter) Collect(c chan<- prometheus.Metric) { insts, err := e.client.GetCounters() if err == nil { e.up.With(prometheus.Labels{"host": e.hostname}).Set(1) for i := range insts { if d, ok := counters[insts[i].Key]; ok { c <- prometheus.MustNewConstMetric(d, prometheus.CounterValue, insts[i].Value, e.labels.Values...) } } } else { e.up.With(prometheus.Labels{"host": e.hostname}).Set(0) log.Println("Could not fetch counter metrics from squid instance: ", err) } if ExtractServiceTimes { insts, err = e.client.GetServiceTimes() if err == nil { for i := range insts { if d, ok := serviceTimes[insts[i].Key]; ok { c <- prometheus.MustNewConstMetric(d, prometheus.GaugeValue, insts[i].Value, e.labels.Values...) } } } else { log.Println("Could not fetch service times metrics from squid instance: ", err) } } insts, err = e.client.GetInfos() if err == nil { for i := range insts { if d, ok := infos[insts[i].Key]; ok { c <- prometheus.MustNewConstMetric(d, prometheus.GaugeValue, insts[i].Value, e.labels.Values...) } else if insts[i].Key == "squid_info" { infoMetricName := prometheus.BuildFQName(namespace, "info", "service") var labelsKeys []string var labelsValues []string for z := range insts[i].VarLabels { labelsKeys = append(labelsKeys, insts[i].VarLabels[z].Key) labelsValues = append(labelsValues, insts[i].VarLabels[z].Value) } infoDesc := prometheus.NewDesc( infoMetricName, "Metrics as string from info on cache_object", labelsKeys, nil, ) c <- prometheus.MustNewConstMetric(infoDesc, prometheus.GaugeValue, insts[i].Value, labelsValues...) } } } else { log.Println("Could not fetch info metrics from squid instance: ", err) } e.up.Collect(c) } prometheus-squid-exporter-1.12.0/collector/service_times.go000066400000000000000000000155541463635037700241650ustar00rootroot00000000000000package collector import ( "fmt" "strings" "github.com/prometheus/client_golang/prometheus" ) type squidServiceTimes struct { Section string Counter string Suffix string Description string } var squidServiceTimess = []squidServiceTimes{ {"HTTP_Requests", "All", "5", "Service Time Percentiles 5min"}, {"HTTP_Requests", "All", "10", "Service Time Percentiles 5min"}, {"HTTP_Requests", "All", "15", "Service Time Percentiles 5min"}, {"HTTP_Requests", "All", "20", "Service Time Percentiles 5min"}, {"HTTP_Requests", "All", "25", "Service Time Percentiles 5min"}, {"HTTP_Requests", "All", "30", "Service Time Percentiles 5min"}, {"HTTP_Requests", "All", "35", "Service Time Percentiles 5min"}, {"HTTP_Requests", "All", "40", "Service Time Percentiles 5min"}, {"HTTP_Requests", "All", "45", "Service Time Percentiles 5min"}, {"HTTP_Requests", "All", "50", "Service Time Percentiles 5min"}, {"HTTP_Requests", "All", "55", "Service Time Percentiles 5min"}, {"HTTP_Requests", "All", "60", "Service Time Percentiles 5min"}, {"HTTP_Requests", "All", "65", "Service Time Percentiles 5min"}, {"HTTP_Requests", "All", "70", "Service Time Percentiles 5min"}, {"HTTP_Requests", "All", "75", "Service Time Percentiles 5min"}, {"HTTP_Requests", "All", "80", "Service Time Percentiles 5min"}, {"HTTP_Requests", "All", "85", "Service Time Percentiles 5min"}, {"HTTP_Requests", "All", "90", "Service Time Percentiles 5min"}, {"HTTP_Requests", "All", "95", "Service Time Percentiles 5min"}, {"HTTP_Requests", "All", "100", "Service Time Percentiles 5min"}, {"Cache_Misses", "", "5", "Service Time Percentiles 5min"}, {"Cache_Misses", "", "10", "Service Time Percentiles 5min"}, {"Cache_Misses", "", "15", "Service Time Percentiles 5min"}, {"Cache_Misses", "", "20", "Service Time Percentiles 5min"}, {"Cache_Misses", "", "25", "Service Time Percentiles 5min"}, {"Cache_Misses", "", "30", "Service Time Percentiles 5min"}, {"Cache_Misses", "", "35", "Service Time Percentiles 5min"}, {"Cache_Misses", "", "40", "Service Time Percentiles 5min"}, {"Cache_Misses", "", "45", "Service Time Percentiles 5min"}, {"Cache_Misses", "", "50", "Service Time Percentiles 5min"}, {"Cache_Misses", "", "55", "Service Time Percentiles 5min"}, {"Cache_Misses", "", "60", "Service Time Percentiles 5min"}, {"Cache_Misses", "", "65", "Service Time Percentiles 5min"}, {"Cache_Misses", "", "70", "Service Time Percentiles 5min"}, {"Cache_Misses", "", "75", "Service Time Percentiles 5min"}, {"Cache_Misses", "", "80", "Service Time Percentiles 5min"}, {"Cache_Misses", "", "85", "Service Time Percentiles 5min"}, {"Cache_Misses", "", "90", "Service Time Percentiles 5min"}, {"Cache_Misses", "", "95", "Service Time Percentiles 5min"}, {"Cache_Hits", "", "5", "Service Time Percentiles 5min"}, {"Cache_Hits", "", "10", "Service Time Percentiles 5min"}, {"Cache_Hits", "", "15", "Service Time Percentiles 5min"}, {"Cache_Hits", "", "20", "Service Time Percentiles 5min"}, {"Cache_Hits", "", "25", "Service Time Percentiles 5min"}, {"Cache_Hits", "", "30", "Service Time Percentiles 5min"}, {"Cache_Hits", "", "35", "Service Time Percentiles 5min"}, {"Cache_Hits", "", "40", "Service Time Percentiles 5min"}, {"Cache_Hits", "", "45", "Service Time Percentiles 5min"}, {"Cache_Hits", "", "50", "Service Time Percentiles 5min"}, {"Cache_Hits", "", "55", "Service Time Percentiles 5min"}, {"Cache_Hits", "", "60", "Service Time Percentiles 5min"}, {"Cache_Hits", "", "65", "Service Time Percentiles 5min"}, {"Cache_Hits", "", "70", "Service Time Percentiles 5min"}, {"Cache_Hits", "", "75", "Service Time Percentiles 5min"}, {"Cache_Hits", "", "80", "Service Time Percentiles 5min"}, {"Cache_Hits", "", "85", "Service Time Percentiles 5min"}, {"Cache_Hits", "", "90", "Service Time Percentiles 5min"}, {"Cache_Hits", "", "95", "Service Time Percentiles 5min"}, {"Near_Hits", "", "5", "Service Time Percentiles 5min"}, {"Near_Hits", "", "10", "Service Time Percentiles 5min"}, {"Near_Hits", "", "15", "Service Time Percentiles 5min"}, {"Near_Hits", "", "20", "Service Time Percentiles 5min"}, {"Near_Hits", "", "25", "Service Time Percentiles 5min"}, {"Near_Hits", "", "30", "Service Time Percentiles 5min"}, {"Near_Hits", "", "35", "Service Time Percentiles 5min"}, {"Near_Hits", "", "40", "Service Time Percentiles 5min"}, {"Near_Hits", "", "45", "Service Time Percentiles 5min"}, {"Near_Hits", "", "50", "Service Time Percentiles 5min"}, {"Near_Hits", "", "55", "Service Time Percentiles 5min"}, {"Near_Hits", "", "60", "Service Time Percentiles 5min"}, {"Near_Hits", "", "65", "Service Time Percentiles 5min"}, {"Near_Hits", "", "70", "Service Time Percentiles 5min"}, {"Near_Hits", "", "75", "Service Time Percentiles 5min"}, {"Near_Hits", "", "80", "Service Time Percentiles 5min"}, {"Near_Hits", "", "85", "Service Time Percentiles 5min"}, {"Near_Hits", "", "90", "Service Time Percentiles 5min"}, {"Near_Hits", "", "95", "Service Time Percentiles 5min"}, {"DNS_Lookups", "", "5", "Service Time Percentiles 5min"}, {"DNS_Lookups", "", "10", "Service Time Percentiles 5min"}, {"DNS_Lookups", "", "15", "Service Time Percentiles 5min"}, {"DNS_Lookups", "", "20", "Service Time Percentiles 5min"}, {"DNS_Lookups", "", "25", "Service Time Percentiles 5min"}, {"DNS_Lookups", "", "30", "Service Time Percentiles 5min"}, {"DNS_Lookups", "", "35", "Service Time Percentiles 5min"}, {"DNS_Lookups", "", "40", "Service Time Percentiles 5min"}, {"DNS_Lookups", "", "45", "Service Time Percentiles 5min"}, {"DNS_Lookups", "", "50", "Service Time Percentiles 5min"}, {"DNS_Lookups", "", "55", "Service Time Percentiles 5min"}, {"DNS_Lookups", "", "60", "Service Time Percentiles 5min"}, {"DNS_Lookups", "", "65", "Service Time Percentiles 5min"}, {"DNS_Lookups", "", "70", "Service Time Percentiles 5min"}, {"DNS_Lookups", "", "75", "Service Time Percentiles 5min"}, {"DNS_Lookups", "", "80", "Service Time Percentiles 5min"}, {"DNS_Lookups", "", "85", "Service Time Percentiles 5min"}, {"DNS_Lookups", "", "90", "Service Time Percentiles 5min"}, {"DNS_Lookups", "", "95", "Service Time Percentiles 5min"}, } func generateSquidServiceTimes(labels []string) descMap { serviceTimes := descMap{} for i := range squidServiceTimess { serviceTime := squidServiceTimess[i] var key string var name string if serviceTime.Counter != "" { key = fmt.Sprintf("%s_%s_%s", serviceTime.Section, serviceTime.Counter, serviceTime.Suffix) name = prometheus.BuildFQName(namespace, strings.Replace(serviceTime.Section, ".", "_", -1), fmt.Sprintf("%s_%s", serviceTime.Counter, serviceTime.Suffix)) } else { key = fmt.Sprintf("%s_%s", serviceTime.Section, serviceTime.Suffix) name = prometheus.BuildFQName(namespace, strings.Replace(serviceTime.Section, ".", "_", -1), fmt.Sprintf("%s", serviceTime.Suffix)) } serviceTimes[key] = prometheus.NewDesc( name, serviceTime.Description, labels, nil, ) } return serviceTimes } prometheus-squid-exporter-1.12.0/config/000077500000000000000000000000001463635037700202425ustar00rootroot00000000000000prometheus-squid-exporter-1.12.0/config/config.go000066400000000000000000000105551463635037700220440ustar00rootroot00000000000000package config import ( "flag" "fmt" "log" "os" "strconv" "strings" ) const ( defaultListenAddress = "127.0.0.1:9301" defaultWebConfigPath = "" defaultListenPort = 9301 defaultMetricsPath = "/metrics" defaultSquidHostname = "localhost" defaultSquidPort = 3128 defaultExtractServiceTimes = true defaultUseProxyHeader = false ) const ( squidExporterListenKey = "SQUID_EXPORTER_LISTEN" squidExporterWebConfigPathKey = "SQUID_EXPORTER_WEB_CONFIG_PATH" squidExporterMetricsPathKey = "SQUID_EXPORTER_METRICS_PATH" squidHostnameKey = "SQUID_HOSTNAME" squidPortKey = "SQUID_PORT" squidLoginKey = "SQUID_LOGIN" squidPasswordKey = "SQUID_PASSWORD" squidPidfile = "SQUID_PIDFILE" squidExtractServiceTimes = "SQUID_EXTRACTSERVICETIMES" squidUseProxyHeader = "SQUID_USE_PROXY_HEADER" ) var ( VersionFlag *bool ) type Labels struct { Keys []string Values []string } /*Config configurations for exporter */ type Config struct { ListenAddress string WebConfigPath string MetricPath string Labels Labels ExtractServiceTimes bool SquidHostname string SquidPort int Login string Password string Pidfile string UseProxyHeader bool } /*NewConfig creates a new config object from command line args */ func NewConfig() *Config { c := &Config{} flag.StringVar(&c.ListenAddress, "listen", loadEnvStringVar(squidExporterListenKey, defaultListenAddress), "Address and Port to bind exporter, in host:port format") flag.StringVar(&c.WebConfigPath, "web.config.file", loadEnvStringVar(squidExporterWebConfigPathKey, defaultWebConfigPath), "Path to configuration file that can enable TLS or authentication. See: https://github.com/prometheus/exporter-toolkit/blob/master/docs/web-configuration.md") flag.StringVar(&c.MetricPath, "metrics-path", loadEnvStringVar(squidExporterMetricsPathKey, defaultMetricsPath), "Metrics path to expose prometheus metrics") flag.BoolVar(&c.ExtractServiceTimes, "extractservicetimes", loadEnvBoolVar(squidExtractServiceTimes, defaultExtractServiceTimes), "Extract service times metrics") flag.Var(&c.Labels, "label", "Custom metrics to attach to metrics, use -label multiple times for each additional label") flag.StringVar(&c.SquidHostname, "squid-hostname", loadEnvStringVar(squidHostnameKey, defaultSquidHostname), "Squid hostname") flag.IntVar(&c.SquidPort, "squid-port", loadEnvIntVar(squidPortKey, defaultSquidPort), "Squid port to read metrics") flag.StringVar(&c.Login, "squid-login", loadEnvStringVar(squidLoginKey, ""), "Login to squid service") flag.StringVar(&c.Password, "squid-password", loadEnvStringVar(squidPasswordKey, ""), "Password to squid service") flag.StringVar(&c.Pidfile, "squid-pidfile", loadEnvStringVar(squidPidfile, ""), "Optional path to the squid PID file for additional metrics") flag.BoolVar(&c.UseProxyHeader, "squid-use-proxy-header", loadEnvBoolVar(squidUseProxyHeader, defaultUseProxyHeader), "Use proxy headers when fetching metrics") VersionFlag = flag.Bool("version", false, "Print the version and exit") flag.Parse() return c } func loadEnvBoolVar(key string, def bool) bool { val := os.Getenv(key) if val == "" { return def } switch strings.ToLower(val) { case "true": return true case "false": return false default: return def } } func loadEnvStringVar(key, def string) string { val := os.Getenv(key) if val == "" { return def } return val } func loadEnvIntVar(key string, def int) int { valStr := os.Getenv(key) if valStr != "" { val, err := strconv.ParseInt(valStr, 0, 32) if err == nil { return int(val) } log.Printf("Error parsing %s='%s'. Integer value expected", key, valStr) } return def } func (l *Labels) String() string { var lbls []string for i := range l.Keys { lbls = append(lbls, l.Keys[i]+"="+l.Values[i]) } return strings.Join(lbls, ", ") } func (l *Labels) Set(value string) error { args := strings.Split(value, "=") if len(args) != 2 || len(args[1]) < 1 { return fmt.Errorf("Label must be in 'key=value' format") } for _, key := range l.Keys { if key == args[0] { return fmt.Errorf("Labels must be distinct, found duplicated key %s", args[0]) } } l.Keys = append(l.Keys, args[0]) l.Values = append(l.Values, args[1]) return nil } prometheus-squid-exporter-1.12.0/dashboards/000077500000000000000000000000001463635037700211075ustar00rootroot00000000000000prometheus-squid-exporter-1.12.0/dashboards/squid-sample-dashboard.json000066400000000000000000000330031463635037700263320ustar00rootroot00000000000000{ "__inputs": [], "__requires": [ { "type": "grafana", "id": "grafana", "name": "Grafana", "version": "5.2.4" }, { "type": "panel", "id": "graph", "name": "Graph", "version": "5.0.0" }, { "type": "panel", "id": "singlestat", "name": "Singlestat", "version": "5.0.0" }, { "type": "panel", "id": "text", "name": "Text", "version": "5.0.0" } ], "annotations": { "list": [ { "builtIn": 1, "datasource": "-- Grafana --", "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", "type": "dashboard" } ] }, "editable": true, "gnetId": null, "graphTooltip": 0, "id": null, "iteration": 1536353270000, "links": [], "panels": [ { "content": "

Squid Sample Dashboard

", "gridPos": { "h": 3, "w": 6, "x": 0, "y": 0 }, "id": 10, "links": [], "mode": "html", "title": "", "type": "text" }, { "cacheTimeout": null, "colorBackground": true, "colorValue": false, "colors": [ "#d44a3a", "rgba(237, 129, 40, 0.89)", "#299c46" ], "datasource": "$datasource", "format": "none", "gauge": { "maxValue": 100, "minValue": 0, "show": false, "thresholdLabels": false, "thresholdMarkers": true }, "gridPos": { "h": 3, "w": 6, "x": 6, "y": 0 }, "id": 14, "interval": null, "links": [], "mappingType": 1, "mappingTypes": [ { "name": "value to text", "value": 1 }, { "name": "range to text", "value": 2 } ], "maxDataPoints": 100, "nullPointMode": "connected", "nullText": null, "postfix": "", "postfixFontSize": "200%", "prefix": "", "prefixFontSize": "200%", "rangeMaps": [ { "from": "null", "text": "N/A", "to": "null" } ], "sparkline": { "fillColor": "rgba(31, 118, 189, 0.18)", "full": false, "lineColor": "rgb(31, 120, 193)", "show": true }, "tableColumn": "", "targets": [ { "expr": "up{job='squid'}", "format": "time_series", "intervalFactor": 1, "legendFormat": "", "refId": "A" } ], "thresholds": "0,1", "title": "Services Up", "type": "singlestat", "valueFontSize": "200%", "valueMaps": [ { "op": "=", "text": "N/A", "value": "null" } ], "valueName": "current" }, { "cacheTimeout": null, "colorBackground": false, "colorValue": false, "colors": [ "#299c46", "rgba(237, 129, 40, 0.89)", "#d44a3a" ], "datasource": "$datasource", "format": "percentunit", "gauge": { "maxValue": 100, "minValue": 0, "show": false, "thresholdLabels": false, "thresholdMarkers": true }, "gridPos": { "h": 3, "w": 4, "x": 12, "y": 0 }, "id": 8, "interval": null, "links": [], "mappingType": 1, "mappingTypes": [ { "name": "value to text", "value": 1 }, { "name": "range to text", "value": 2 } ], "maxDataPoints": 100, "nullPointMode": "connected", "nullText": null, "postfix": "", "postfixFontSize": "50%", "prefix": "", "prefixFontSize": "50%", "rangeMaps": [ { "from": "null", "text": "N/A", "to": "null" } ], "sparkline": { "fillColor": "rgba(31, 118, 189, 0.18)", "full": false, "lineColor": "rgb(31, 120, 193)", "show": false }, "tableColumn": "", "targets": [ { "expr": "squid_client_http_hit_kbytes_out_bytes_total / squid_client_http_kbytes_out_kbytes_total", "format": "time_series", "intervalFactor": 1, "refId": "A" } ], "thresholds": "", "title": "Bytes Hit Rate", "type": "singlestat", "valueFontSize": "80%", "valueMaps": [ { "op": "=", "text": "N/A", "value": "null" } ], "valueName": "avg" }, { "cacheTimeout": null, "colorBackground": false, "colorValue": false, "colors": [ "#299c46", "rgba(237, 129, 40, 0.89)", "#d44a3a" ], "datasource": "$datasource", "format": "percentunit", "gauge": { "maxValue": 100, "minValue": 0, "show": false, "thresholdLabels": false, "thresholdMarkers": true }, "gridPos": { "h": 3, "w": 4, "x": 16, "y": 0 }, "id": 6, "interval": null, "links": [], "mappingType": 1, "mappingTypes": [ { "name": "value to text", "value": 1 }, { "name": "range to text", "value": 2 } ], "maxDataPoints": 100, "nullPointMode": "connected", "nullText": null, "postfix": "", "postfixFontSize": "50%", "prefix": "", "prefixFontSize": "50%", "rangeMaps": [ { "from": "null", "text": "N/A", "to": "null" } ], "sparkline": { "fillColor": "rgba(31, 118, 189, 0.18)", "full": false, "lineColor": "rgb(31, 120, 193)", "show": false }, "tableColumn": "", "targets": [ { "expr": "squid_client_http_hits_total / squid_client_http_requests_total", "format": "time_series", "intervalFactor": 1, "legendFormat": "Hit rate", "refId": "A" } ], "thresholds": "", "title": "Catch Hit Rate", "type": "singlestat", "valueFontSize": "80%", "valueMaps": [ { "op": "=", "text": "N/A", "value": "null" } ], "valueName": "avg" }, { "cacheTimeout": null, "colorBackground": false, "colorValue": false, "colors": [ "#299c46", "rgba(237, 129, 40, 0.89)", "#d44a3a" ], "datasource": "$datasource", "format": "percentunit", "gauge": { "maxValue": 100, "minValue": 0, "show": false, "thresholdLabels": false, "thresholdMarkers": true }, "gridPos": { "h": 3, "w": 4, "x": 20, "y": 0 }, "id": 12, "interval": null, "links": [], "mappingType": 1, "mappingTypes": [ { "name": "value to text", "value": 1 }, { "name": "range to text", "value": 2 } ], "maxDataPoints": 100, "nullPointMode": "connected", "nullText": null, "postfix": "", "postfixFontSize": "50%", "prefix": "", "prefixFontSize": "50%", "rangeMaps": [ { "from": "null", "text": "N/A", "to": "null" } ], "sparkline": { "fillColor": "rgba(31, 118, 189, 0.18)", "full": false, "lineColor": "rgb(31, 120, 193)", "show": false }, "tableColumn": "", "targets": [ { "expr": "squid_client_http_errors_total / squid_client_http_requests_total", "format": "time_series", "intervalFactor": 1, "refId": "A" } ], "thresholds": "", "title": "Error Rate", "type": "singlestat", "valueFontSize": "80%", "valueMaps": [ { "op": "=", "text": "N/A", "value": "null" } ], "valueName": "avg" }, { "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, "datasource": "$datasource", "fill": 1, "gridPos": { "h": 6, "w": 12, "x": 0, "y": 3 }, "id": 2, "legend": { "avg": false, "current": false, "max": false, "min": false, "show": true, "total": false, "values": false }, "lines": true, "linewidth": 1, "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "expr": "rate(squid_client_http_requests_total[5m])", "format": "time_series", "interval": "", "intervalFactor": 1, "legendFormat": "Total Requests", "refId": "B" }, { "expr": "rate(squid_client_http_hits_total[5m])", "format": "time_series", "interval": "", "intervalFactor": 1, "legendFormat": "Total HTTP hits", "refId": "A" }, { "expr": "rate(squid_client_http_errors_total[5m])", "format": "time_series", "intervalFactor": 1, "legendFormat": "Total Errors", "refId": "C" } ], "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Client Requests", "tooltip": { "shared": true, "sort": 0, "value_type": "individual" }, "type": "graph", "xaxis": { "buckets": null, "mode": "time", "name": null, "show": true, "values": [] }, "yaxes": [ { "format": "reqps", "label": null, "logBase": 1, "max": null, "min": null, "show": true }, { "format": "short", "label": null, "logBase": 1, "max": null, "min": null, "show": true } ], "yaxis": { "align": false, "alignLevel": null } }, { "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, "datasource": "$datasource", "fill": 1, "gridPos": { "h": 6, "w": 12, "x": 12, "y": 3 }, "id": 4, "legend": { "avg": false, "current": false, "max": false, "min": false, "show": true, "total": false, "values": false }, "lines": true, "linewidth": 1, "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "expr": "rate(squid_client_http_kbytes_in_kbytes_total[5m]) * -1", "format": "time_series", "interval": "", "intervalFactor": 1, "legendFormat": "HTTP Traffic in", "refId": "A" }, { "expr": "rate(squid_client_http_kbytes_out_kbytes_total[5m])", "format": "time_series", "intervalFactor": 1, "legendFormat": "HTTP Traffic Out", "refId": "B" }, { "expr": "rate(squid_client_http_hit_kbytes_out_bytes_total[5m])", "format": "time_series", "interval": "", "intervalFactor": 1, "legendFormat": "HTTP Traffic Out Hits", "refId": "C" } ], "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Client Traffic", "tooltip": { "shared": true, "sort": 0, "value_type": "individual" }, "type": "graph", "xaxis": { "buckets": null, "mode": "time", "name": null, "show": true, "values": [] }, "yaxes": [ { "format": "KBs", "label": null, "logBase": 1, "max": null, "min": null, "show": true }, { "format": "short", "label": null, "logBase": 1, "max": null, "min": null, "show": true } ], "yaxis": { "align": false, "alignLevel": null } } ], "schemaVersion": 16, "style": "dark", "tags": [], "templating": { "list": [ { "current": { "text": "default", "value": "default" }, "hide": 0, "label": null, "name": "datasource", "options": [], "query": "prometheus", "refresh": 1, "regex": "", "type": "datasource" } ] }, "time": { "from": "now-6h", "to": "now" }, "timepicker": { "refresh_intervals": [ "5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d" ], "time_options": [ "5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d" ] }, "timezone": "", "title": "Squid Sample Dashboard", "uid": "yRc_Bj2ik", "version": 3 }prometheus-squid-exporter-1.12.0/deploy.sh000066400000000000000000000005371463635037700206320ustar00rootroot00000000000000#!/bin/bash # Travis Docker deploy script IMAGE_NAME="boynux/squid-exporter" IMAGE_TAG=${TRAVIS_TAG:-latest} docker --version docker build -t $IMAGE_NAME:$IMAGE_TAG . docker tag $IMAGE_NAME:$IMAGE_TAG $IMAGE_NAME:latest echo $DOCKER_API_KEY | docker login -u boynux --password-stdin docker push $IMAGE_NAME:$IMAGE_TAG docker push $IMAGE_NAME:latest prometheus-squid-exporter-1.12.0/go.mod000066400000000000000000000024441463635037700201070ustar00rootroot00000000000000module github.com/boynux/squid-exporter go 1.20 require ( github.com/go-kit/log v0.2.1 github.com/pires/go-proxyproto v0.6.2 github.com/prometheus/client_golang v1.19.0 github.com/prometheus/common v0.53.0 github.com/prometheus/exporter-toolkit v0.11.0 github.com/stretchr/testify v1.7.0 ) require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/jpillora/backoff v1.0.0 // indirect github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.6.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect golang.org/x/crypto v0.21.0 // indirect golang.org/x/net v0.22.0 // indirect golang.org/x/oauth2 v0.18.0 // indirect golang.org/x/sync v0.5.0 // indirect golang.org/x/sys v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect ) prometheus-squid-exporter-1.12.0/go.sum000066400000000000000000000147721463635037700201430ustar00rootroot00000000000000github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/pires/go-proxyproto v0.6.2 h1:KAZ7UteSOt6urjme6ZldyFm4wDe/z0ZUP0Yv0Dos0d8= github.com/pires/go-proxyproto v0.6.2/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos= github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8= github.com/prometheus/common v0.53.0 h1:U2pL9w9nmJwJDa4qqLQ3ZaePJ6ZTwt7cMD3AG3+aLCE= github.com/prometheus/common v0.53.0/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U= github.com/prometheus/exporter-toolkit v0.11.0 h1:yNTsuZ0aNCNFQ3aFTD2uhPOvr4iD7fdBvKPAEGkNf+g= github.com/prometheus/exporter-toolkit v0.11.0/go.mod h1:BVnENhnNecpwoTLiABx7mrPB/OLRIgN74qlQbV+FK1Q= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= prometheus-squid-exporter-1.12.0/helm/000077500000000000000000000000001463635037700177225ustar00rootroot00000000000000prometheus-squid-exporter-1.12.0/helm/.helmignore000066400000000000000000000005351463635037700220570ustar00rootroot00000000000000# Patterns to ignore when building packages. # This supports shell glob matching, relative path matching, and # negation (prefixed with !). Only one pattern per line. .DS_Store # Common VCS dirs .git/ .gitignore .bzr/ .bzrignore .hg/ .hgignore .svn/ # Common backup files *.swp *.bak *.tmp *.orig *~ # Various IDEs .project .idea/ *.tmproj .vscode/ prometheus-squid-exporter-1.12.0/helm/Chart.yaml000066400000000000000000000002171463635037700216470ustar00rootroot00000000000000apiVersion: v2 name: squid-exporter description: A helm chart to deploy Squid Exporter. type: application version: 0.1.0 appVersion: "v1.10.3" prometheus-squid-exporter-1.12.0/helm/templates/000077500000000000000000000000001463635037700217205ustar00rootroot00000000000000prometheus-squid-exporter-1.12.0/helm/templates/NOTES.txt000066400000000000000000000033571463635037700233610ustar00rootroot000000000000001. Get the application URL by running these commands: {{- if .Values.ingress.enabled }} {{- range $host := .Values.ingress.hosts }} {{- range .paths }} http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} {{- end }} {{- end }} {{- else if contains "NodePort" .Values.service.type }} export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "squid-exporter.fullname" . }}) export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") echo http://$NODE_IP:$NODE_PORT {{- else if contains "LoadBalancer" .Values.service.type }} NOTE: It may take a few minutes for the LoadBalancer IP to be available. You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "squid-exporter.fullname" . }}' export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "squid-exporter.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") echo http://$SERVICE_IP:{{ .Values.service.port }} {{- else if contains "ClusterIP" .Values.service.type }} export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "squid-exporter.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") echo "Visit http://127.0.0.1:8080 to use your application" kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT {{- end }} prometheus-squid-exporter-1.12.0/helm/templates/_helpers.tpl000066400000000000000000000037261463635037700242520ustar00rootroot00000000000000{{/* Expand the name of the chart. */}} {{- define "squid-exporter.name" -}} {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} {{- end }} {{/* Create a default fully qualified app name. We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). If release name contains chart name it will be used as a full name. */}} {{- define "squid-exporter.fullname" -}} {{- if .Values.fullnameOverride }} {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} {{- else }} {{- $name := default .Chart.Name .Values.nameOverride }} {{- if contains $name .Release.Name }} {{- .Release.Name | trunc 63 | trimSuffix "-" }} {{- else }} {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} {{- end }} {{- end }} {{- end }} {{/* Create chart name and version as used by the chart label. */}} {{- define "squid-exporter.chart" -}} {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} {{- end }} {{/* Common labels */}} {{- define "squid-exporter.labels" -}} helm.sh/chart: {{ include "squid-exporter.chart" . }} {{ include "squid-exporter.selectorLabels" . }} {{- if .Chart.AppVersion }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} app.kubernetes.io/managed-by: {{ .Release.Service }} squid-exporter.boynux.com/proxy-hostname: {{ .Values.squidConfig.hostname }} squid-exporter.boynux.com/proxy-port: {{ .Values.squidConfig.port | quote }} {{- end }} {{/* Selector labels */}} {{- define "squid-exporter.selectorLabels" -}} app.kubernetes.io/name: {{ include "squid-exporter.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }} {{/* Create the name of the service account to use */}} {{- define "squid-exporter.serviceAccountName" -}} {{- if .Values.serviceAccount.create }} {{- default (include "squid-exporter.fullname" .) .Values.serviceAccount.name }} {{- else }} {{- default "default" .Values.serviceAccount.name }} {{- end }} {{- end }} prometheus-squid-exporter-1.12.0/helm/templates/deployment.yaml000066400000000000000000000046061463635037700247720ustar00rootroot00000000000000apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "squid-exporter.fullname" . }} labels: {{- include "squid-exporter.labels" . | nindent 4 }} spec: replicas: {{ .Values.replicaCount }} selector: matchLabels: {{- include "squid-exporter.selectorLabels" . | nindent 6 }} template: metadata: annotations: checksum/config: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }} {{- with .Values.podAnnotations }} {{- toYaml . | nindent 8 }} {{- end }} labels: {{- include "squid-exporter.selectorLabels" . | nindent 8 }} spec: {{- with .Values.imagePullSecrets }} imagePullSecrets: {{- toYaml . | nindent 8 }} {{- end }} serviceAccountName: {{ include "squid-exporter.serviceAccountName" . }} securityContext: {{- toYaml .Values.podSecurityContext | nindent 8 }} containers: - name: {{ .Chart.Name }} securityContext: {{- toYaml .Values.securityContext | nindent 12 }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} envFrom: - secretRef: name: {{ include "squid-exporter.fullname" . }} env: - name: SQUID_EXPORTER_LISTEN value: "0.0.0.0:9301" - name: SQUID_EXPORTER_METRICS_PATH value: /metrics - name: SQUID_HOSTNAME value: {{ required ".Values.squidConfig.hostname must be defined" .Values.squidConfig.hostname }} - name: SQUID_PORT value: {{ .Values.squidConfig.port | quote }} ports: - name: metrics containerPort: 9301 protocol: TCP livenessProbe: httpGet: path: / port: metrics readinessProbe: httpGet: path: / port: metrics resources: {{- toYaml .Values.resources | nindent 12 }} {{- with .Values.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} {{- end }} {{- with .Values.affinity }} affinity: {{- toYaml . | nindent 8 }} {{- end }} {{- with .Values.tolerations }} tolerations: {{- toYaml . | nindent 8 }} {{- end }} prometheus-squid-exporter-1.12.0/helm/templates/ingress.yaml000066400000000000000000000040551463635037700242620ustar00rootroot00000000000000{{- if .Values.ingress.enabled -}} {{- $fullName := include "squid-exporter.fullname" . -}} {{- $svcPort := .Values.service.port -}} {{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} {{- end }} {{- end }} {{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} apiVersion: networking.k8s.io/v1 {{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} apiVersion: networking.k8s.io/v1beta1 {{- else -}} apiVersion: extensions/v1beta1 {{- end }} kind: Ingress metadata: name: {{ $fullName }} labels: {{- include "squid-exporter.labels" . | nindent 4 }} {{- with .Values.ingress.annotations }} annotations: {{- toYaml . | nindent 4 }} {{- end }} spec: {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} ingressClassName: {{ .Values.ingress.className }} {{- end }} {{- if .Values.ingress.tls }} tls: {{- range .Values.ingress.tls }} - hosts: {{- range .hosts }} - {{ . | quote }} {{- end }} secretName: {{ .secretName }} {{- end }} {{- end }} rules: {{- range .Values.ingress.hosts }} - host: {{ .host | quote }} http: paths: {{- range .paths }} - path: {{ .path }} {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} pathType: {{ .pathType }} {{- end }} backend: {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} service: name: {{ $fullName }} port: number: {{ $svcPort }} {{- else }} serviceName: {{ $fullName }} servicePort: {{ $svcPort }} {{- end }} {{- end }} {{- end }} {{- end }} prometheus-squid-exporter-1.12.0/helm/templates/prometheusrules.yaml000066400000000000000000000007351463635037700260570ustar00rootroot00000000000000{{- if .Values.serviceMonitor.rules }} apiVersion: monitoring.coreos.com/v1 kind: PrometheusRule metadata: name: {{ include "squid-exporter.fullname" . }} labels: {{- include "squid-exporter.labels" . | nindent 4 }} {{- with .Values.serviceMonitor.labels }} {{- . | toYaml | nindent 4 }} {{- end }} spec: groups: - name: {{ include "squid-exporter.fullname" . }} rules: {{- .Values.serviceMonitor.rules | toYaml | nindent 8 }} {{- end }}prometheus-squid-exporter-1.12.0/helm/templates/secret.yaml000066400000000000000000000004261463635037700240730ustar00rootroot00000000000000apiVersion: v1 kind: Secret metadata: name: {{ include "squid-exporter.fullname" . }} labels: {{- include "squid-exporter.labels" . | nindent 4 }} data: SQUID_LOGIN: {{ .Values.squidConfig.login | b64enc }} SQUID_PASSWORD: {{ .Values.squidConfig.password | b64enc }}prometheus-squid-exporter-1.12.0/helm/templates/service.yaml000066400000000000000000000014151463635037700242450ustar00rootroot00000000000000apiVersion: v1 kind: Service metadata: name: {{ include "squid-exporter.fullname" . }} labels: {{- include "squid-exporter.labels" . | nindent 4 }} spec: type: {{ .Values.service.type }} {{- if or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer") }} externalTrafficPolicy: {{ .Values.service.externalTrafficPolicy }} {{- end }} sessionAffinity: {{ .Values.service.sessionAffinity }} ports: - port: {{ .Values.service.port }} targetPort: metrics protocol: TCP name: metrics {{- if or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer") }} nodePort: {{ .Values.service.nodePort }} {{- end }} selector: {{- include "squid-exporter.selectorLabels" . | nindent 4 }} prometheus-squid-exporter-1.12.0/helm/templates/serviceaccount.yaml000066400000000000000000000005161463635037700256230ustar00rootroot00000000000000{{- if .Values.serviceAccount.create -}} apiVersion: v1 kind: ServiceAccount metadata: name: {{ include "squid-exporter.serviceAccountName" . }} labels: {{- include "squid-exporter.labels" . | nindent 4 }} {{- with .Values.serviceAccount.annotations }} annotations: {{- toYaml . | nindent 4 }} {{- end }} {{- end }} prometheus-squid-exporter-1.12.0/helm/templates/servicemonitor.yaml000066400000000000000000000017421463635037700256600ustar00rootroot00000000000000apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: name: {{ include "squid-exporter.fullname" . }} labels: {{- include "squid-exporter.labels" . | nindent 4 }} {{- with .Values.serviceMonitor.labels }} {{- . | toYaml | nindent 4 }} {{- end }} spec: endpoints: - port: metrics interval: {{ .Values.serviceMonitor.interval }} metricRelabelings: {{- .Values.serviceMonitor.additionalMetricsRelabels | toYaml | nindent 10 }} path: "/metrics" relabelings: {{- .Values.serviceMonitor.additionalRelabeling | toYaml | nindent 10 }} scheme: http scrapeTimeout: {{ .Values.serviceMonitor.scrapeTimeout }} targetLabels: {{- range $label, $val := .Values.serviceMonitor.labels }} - {{ $label }} {{- end }} - squid-exporter.boynux.com/proxy-hostname - squid-exporter.boynux.com/proxy-port selector: matchLabels: {{- include "squid-exporter.selectorLabels" . | nindent 8 }}prometheus-squid-exporter-1.12.0/helm/values.yaml000066400000000000000000000065471463635037700221210ustar00rootroot00000000000000# Default values for squid-exporter. # This is a YAML-formatted file. # Declare variables to be passed into your templates. squidConfig: login: "" password: "" hostname: "" port: 3128 # Under normal circumstances one replica is needed. replicaCount: 1 image: repository: boynux/squid-exporter pullPolicy: IfNotPresent # Overrides the image tag whose default is the chart appVersion. tag: "" imagePullSecrets: [] nameOverride: "" fullnameOverride: "" serviceAccount: # Specifies whether a service account should be created create: true # Annotations to add to the service account annotations: {} # The name of the service account to use. # If not set and create is true, a name is generated using the fullname template name: "" podAnnotations: {} podSecurityContext: {} # fsGroup: 2000 securityContext: {} # capabilities: # drop: # - ALL # readOnlyRootFilesystem: true # runAsNonRoot: true # runAsUser: 1000 service: # Kubernetes Service type, one of [ClusterIP, NodePort, LoadBalancer] type: ClusterIP port: 80 # Supports either ClientIP or None. # Used to maintain session affinity. # Enable client IP based session affinity. # https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies sessionAffinity: ClientIP # Service settings below are applicable only if # service.type is one of [LoadBalancer, NodePort] and not ClusterIP. # Possible values are [Cluster, Local]. # If set to Local, then the Service's port will be available only on Kubernetes # Nodes which have the Squid Pods so no Kubernetes Node-to-Node traffic will be forwarded. # If set to Cluster, then the Service's port will be available on any Node of # a Kubernetes cluster. The drawback is that Kubernetes will use double NAT # so it will hide the Client source IP from Squid. externalTrafficPolicy: Cluster # Node port to listen on. Typically, Kubernetes allows ports in range 30000-32767 # see https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport # for more information. nodePort: "" ingress: enabled: false className: "" annotations: {} # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" hosts: - host: chart-example.local paths: - path: / pathType: ImplementationSpecific tls: [] # - secretName: chart-example-tls # hosts: # - chart-example.local resources: {} # We usually recommend not to specify default resources and to leave this as a conscious # choice for the user. This also increases chances charts run on environments with little # resources, such as Minikube. If you do want to specify resources, uncomment the following # lines, adjust them as necessary, and remove the curly braces after 'resources:'. # limits: # cpu: 100m # memory: 128Mi # requests: # cpu: 100m # memory: 128Mi nodeSelector: {} tolerations: [] affinity: {} serviceMonitor: enabled: false additionalMetricsRelabels: [] additionalRelabeling: [] labels: {} interval: 30s scrapeTimeout: 30s # Prometheus Operator rules to install rules: [] # - alert: SquidDown # annotations: # message: Exporter can not collect metrics from Squid proxy server {{ $labels.host }} # expr: squid_up == 0 # for: 5m # labels: # severity: critical prometheus-squid-exporter-1.12.0/helpers.go000066400000000000000000000024741463635037700207750ustar00rootroot00000000000000package main import ( "log" "net" "strconv" "strings" "github.com/boynux/squid-exporter/config" proxyproto "github.com/pires/go-proxyproto" ) func createProxyHeader(cfg *config.Config) string { la := strings.Split(cfg.ListenAddress, ":") if len(la) < 2 { log.Printf("Cannot parse listen address (%s). Failed to create proxy header\n", cfg.ListenAddress) return "" } spt, err := strconv.Atoi(la[1]) if err != nil { log.Printf("Failed to create proxy header: %v\n", err.Error()) return "" } sip, err := net.LookupIP(la[0]) if err != nil { log.Printf("Failed to create proxy header: %v\n", err.Error()) return "" } dip, err := net.LookupIP(cfg.SquidHostname) if err != nil { log.Printf("Failed to create proxy header: %v\n", err.Error()) return "" } ph := &proxyproto.Header{ Version: 1, Command: proxyproto.PROXY, TransportProtocol: proxyproto.TCPv4, SourceAddr: &net.TCPAddr{ IP: sip[0], Port: spt, }, DestinationAddr: &net.TCPAddr{ IP: dip[0], Port: cfg.SquidPort, }, } phs, err := ph.Format() if err != nil { log.Printf("Failed to create proxy header: %v\n", err.Error()) } // proxyproto adds crlf to the end of the header string, but we will add this later // we are triming it here. return strings.TrimSuffix(string(phs), "\r\n") } prometheus-squid-exporter-1.12.0/helpers_test.go000066400000000000000000000007031463635037700220250ustar00rootroot00000000000000package main import ( "testing" "github.com/stretchr/testify/assert" "github.com/boynux/squid-exporter/config" ) func TestCreatProxyHelper(t *testing.T) { cfg := &config.Config{ ListenAddress: "192.0.2.1:3192", SquidHostname: "127.0.0.1", SquidPort: 3128, } expectedHProxyString := "PROXY TCP4 192.0.2.1 127.0.0.1 3192 3128" p := createProxyHeader(cfg) assert.Equal(t, expectedHProxyString, p, "Proxy headers do not match!") } prometheus-squid-exporter-1.12.0/main.go000066400000000000000000000051741463635037700202570ustar00rootroot00000000000000package main import ( "fmt" "log" "net/http" "os" "strconv" "strings" "github.com/boynux/squid-exporter/collector" "github.com/boynux/squid-exporter/config" kitlog "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/collectors" versioncollector "github.com/prometheus/client_golang/prometheus/collectors/version" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/common/version" "github.com/prometheus/exporter-toolkit/web" ) func init() { prometheus.MustRegister(versioncollector.NewCollector("squid_exporter")) } func main() { cfg := config.NewConfig() if *config.VersionFlag { log.Println(version.Print("squid_exporter")) os.Exit(0) } collector.ExtractServiceTimes = cfg.ExtractServiceTimes headers := []string{} if cfg.UseProxyHeader { headers = append(headers, createProxyHeader(cfg)) } log.Println("Scraping metrics from", fmt.Sprintf("%s:%d", cfg.SquidHostname, cfg.SquidPort)) e := collector.New(&collector.CollectorConfig{ Hostname: cfg.SquidHostname, Port: cfg.SquidPort, Login: cfg.Login, Password: cfg.Password, Labels: cfg.Labels, Headers: headers, }) prometheus.MustRegister(e) if cfg.Pidfile != "" { procExporter := collectors.NewProcessCollector(collectors.ProcessCollectorOpts{ PidFn: func() (int, error) { content, err := os.ReadFile(cfg.Pidfile) if err != nil { return 0, fmt.Errorf("can't read pid file %q: %s", cfg.Pidfile, err) } value, err := strconv.Atoi(strings.TrimSpace(string(content))) if err != nil { return 0, fmt.Errorf("can't parse pid file %q: %s", cfg.Pidfile, err) } return value, nil }, Namespace: "squid", }) prometheus.MustRegister(procExporter) } // Serve metrics http.Handle(cfg.MetricPath, promhttp.Handler()) if cfg.MetricPath != "/" { landingConfig := web.LandingConfig{ Name: "Squid Exporter", Description: "Prometheus exporter for Squid caching proxy servers", HeaderColor: "#15a5be", Version: version.Info(), Links: []web.LandingLinks{ { Address: cfg.MetricPath, Text: "Metrics", }, }, } landingPage, err := web.NewLandingPage(landingConfig) if err != nil { log.Fatal(err) } http.Handle("/", landingPage) } systemdSocket := false toolkitFlags := &web.FlagConfig{ WebListenAddresses: &[]string{cfg.ListenAddress}, WebSystemdSocket: &systemdSocket, WebConfigFile: &cfg.WebConfigPath, } logger := kitlog.NewLogfmtLogger(kitlog.StdlibWriter{}) server := &http.Server{} log.Fatal(web.ListenAndServe(server, toolkitFlags, logger)) } prometheus-squid-exporter-1.12.0/prometheus/000077500000000000000000000000001463635037700211705ustar00rootroot00000000000000prometheus-squid-exporter-1.12.0/prometheus/prometheus.yml000066400000000000000000000016301463635037700241060ustar00rootroot00000000000000# my global config global: scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute. evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute. # scrape_timeout is set to the global default (10s). # Alertmanager configuration alerting: alertmanagers: - static_configs: - targets: # - alertmanager:9093 # Load rules once and periodically evaluate them according to the global 'evaluation_interval'. rule_files: # - "first_rules.yml" # - "second_rules.yml" # A scrape configuration containing exactly one endpoint to scrape: # Here it's Prometheus itself. scrape_configs: # The job name is added as a label `job=` to any timeseries scraped from this config. - job_name: 'squid' # metrics_path defaults to '/metrics' # scheme defaults to 'http' static_configs: - targets: ['localhost:9301'] prometheus-squid-exporter-1.12.0/types/000077500000000000000000000000001463635037700201415ustar00rootroot00000000000000prometheus-squid-exporter-1.12.0/types/types.go000066400000000000000000000004631463635037700216370ustar00rootroot00000000000000package types /*VarLabel maps key value prometheus labes*/ type VarLabel struct { Key string Value string } /*Counter maps a squid conters */ type Counter struct { Key string Value float64 VarLabels []VarLabel } /*Counters is a list of multiple squid counters */ type Counters []Counter