Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parse hex numbers #270

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Taken from https://docs.github.com/en/actions/publishing-packages/publishing-docker-images
name: Create and publish a Docker image

on:
push:

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
build-and-push-image:
runs-on: ubuntu-latest

# Sets the permissions granted to the GITHUB_TOKEN for the actions in this job.
permissions:
contents: read
packages: write

steps:
- name: "Checkout repository"
uses: actions/checkout@v4

- name: Log in to the Container registry
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

- name: Build and push Docker image
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
44 changes: 36 additions & 8 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,12 +1,40 @@
ARG ARCH="amd64"
ARG OS="linux"
FROM quay.io/prometheus/busybox-${OS}-${ARCH}:glibc
LABEL maintainer="The Prometheus Authors <[email protected]>"
# ARG ARCH="arm64"
# ARG OS="linux"
# FROM quay.io/prometheus/busybox-${OS}-${ARCH}:glibc
# LABEL maintainer="The Prometheus Authors <[email protected]>"

ARG ARCH="amd64"
ARG OS="linux"
COPY .build/${OS}-${ARCH}/json_exporter /bin/json_exporter
# ARG ARCH="arm64"
# ARG OS="linux"
# COPY .build/${OS}-${ARCH}/json_exporter /bin/json_exporter

FROM golang:1.19 as builder

# Create and change to the app directory.
WORKDIR /app

# Retrieve application dependencies.
# This allows the container build to reuse cached dependencies.
# Expecting to copy go.mod and if present go.sum.
COPY go.* ./
RUN go mod download

# Copy local code to the container image.
COPY . ./

# Build the binary.
RUN go build -v -o json_exporter

# Use the official Debian slim image for a lean production container.
# https://hub.docker.com/_/debian
# https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
FROM debian:buster-slim
RUN set -x && apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
ca-certificates && \
rm -rf /var/lib/apt/lists/*

# Copy the binary to the production image from the builder stage.
COPY --from=builder /app/json_exporter /app/json_exporter

EXPOSE 7979
USER nobody
ENTRYPOINT [ "/bin/json_exporter" ]
ENTRYPOINT [ "/app/json_exporter" ]
37 changes: 29 additions & 8 deletions exporter/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,17 +57,27 @@ func (mc JSONMetricCollector) Collect(ch chan<- prometheus.Metric) {
continue
}

if floatValue, err := SanitizeValue(value); err == nil {
metric := prometheus.MustNewConstMetric(
floatValue, err := SanitizeValue(value)
if err == nil {
ch <- prometheus.MustNewConstMetric(
m.Desc,
m.ValueType,
floatValue,
extractLabels(mc.Logger, mc.Data, m.LabelsJSONPaths)...,
)
ch <- timestampMetric(mc.Logger, m, mc.Data, metric)
} else {
level.Error(mc.Logger).Log("msg", "Failed to convert extracted value to float64", "path", m.KeyJSONPath, "value", value, "err", err, "metric", m.Desc)
continue
intValue, err := SanitizeHexIntValue(value)
if err == nil {
ch <- prometheus.MustNewConstMetric(
m.Desc,
m.ValueType,
float64(intValue),
extractLabels(mc.Logger, mc.Data, m.LabelsJSONPaths)...,
)
} else {
level.Error(mc.Logger).Log("msg", "Failed to convert extracted value to float64", "path", m.KeyJSONPath, "value", value, "err", err, "metric", m.Desc)
continue
}
}

case config.ObjectScrape:
Expand All @@ -91,7 +101,8 @@ func (mc JSONMetricCollector) Collect(ch chan<- prometheus.Metric) {
continue
}

if floatValue, err := SanitizeValue(value); err == nil {
floatValue, err := SanitizeValue(value)
if err == nil {
metric := prometheus.MustNewConstMetric(
m.Desc,
m.ValueType,
Expand All @@ -100,8 +111,18 @@ func (mc JSONMetricCollector) Collect(ch chan<- prometheus.Metric) {
)
ch <- timestampMetric(mc.Logger, m, jdata, metric)
} else {
level.Error(mc.Logger).Log("msg", "Failed to convert extracted value to float64", "path", m.ValueJSONPath, "value", value, "err", err, "metric", m.Desc)
continue
intValue, err := SanitizeHexIntValue(value)
if err == nil {
ch <- prometheus.MustNewConstMetric(
m.Desc,
m.ValueType,
float64(intValue),
extractLabels(mc.Logger, mc.Data, m.LabelsJSONPaths)...,
)
} else {
level.Error(mc.Logger).Log("msg", "Failed to convert extracted value to float64", "path", m.ValueJSONPath, "value", value, "err", err, "metric", m.Desc)
continue
}
}
}
} else {
Expand Down
17 changes: 17 additions & 0 deletions exporter/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,23 @@ func SanitizeIntValue(s string) (int64, error) {
return value, fmt.Errorf(resultErr)
}

func SanitizeHexIntValue(s string) (int64, error) {
var err error
var value int64
var resultErr string

// remove 0x suffix if found in the input string
cleaned := strings.Replace(s, "0x", "", -1)
cleaned = strings.Replace(cleaned, "\"", "", -1)

if value, err = strconv.ParseInt(cleaned, 16, 64); err == nil {
return value, nil
}
resultErr = fmt.Sprintf("%s", err)

return value, fmt.Errorf(resultErr)
}

func CreateMetricsList(c config.Module) ([]JSONMetric, error) {
var (
metrics []JSONMetric
Expand Down
21 changes: 21 additions & 0 deletions exporter/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,27 @@ func TestSanitizeValue(t *testing.T) {
}
}

func TestSanitizeValueHex(t *testing.T) {
tests := []struct {
Input string
ExpectedOutput int64
ShouldSucceed bool
}{
{"0x1d55195", 30757269, true},
{"\"0x1d55195\"", 30757269, true},
}

for i, test := range tests {
actualOutput, err := SanitizeHexIntValue(test.Input)
if err != nil && test.ShouldSucceed {
t.Fatalf("Value snitization test %d failed with an unexpected error.\nINPUT:\n%q\nERR:\n%s", i, test.Input, err)
}
if test.ShouldSucceed && actualOutput != test.ExpectedOutput {
t.Fatalf("Value sanitization test %d fails unexpectedly.\nGOT:\n%d\nEXPECTED:\n%d", i, actualOutput, test.ExpectedOutput)
}
}
}

func TestSanitizeValueNaN(t *testing.T) {
actualOutput, err := SanitizeValue("<nil>")
if err != nil {
Expand Down