Skip to content

Backend: Set content-type on metrics endpoints - #5042

Merged
lastzero merged 1 commit into
photoprism:developfrom
brandon1024:prom-content-type-fix
Jun 23, 2025
Merged

Backend: Set content-type on metrics endpoints#5042
lastzero merged 1 commit into
photoprism:developfrom
brandon1024:prom-content-type-fix

Conversation

@brandon1024

@brandon1024 brandon1024 commented Jun 1, 2025

Copy link
Copy Markdown
Contributor

Overview

The prometheus text format requires metrics endpoints respond with the content-type text/plain; version=0.0.4. Without this, newer versions of prometheus fail to scrape the metrics endpoint and report an error. It's possible to work around this by setting the fallback_scrape_protocol setting in the prometheus scrape target configuration, but this revision sets the content type appropriately to avoid this in the first place.

Add test for the content type in the response.

Fix Details

With the latest version of Prometheus, after configuring photoprism as a scrape target with standard configuration, prometheus scrapes fail with the error below:

time=2025-06-01T15:33:53.612Z level=ERROR source=scrape.go:1627 msg="Failed to determine correct type of scrape target." component="scrape manager" scrape_pool=photoprism target=http://photoprism:8080/api/v1/metrics content_type="" fallback_media_type="" err="non-compliant scrape target sending blank Content-Type and no fallback_scrape_protocol specified for target"

I've been using the docker compose configuration below for my testing, and you can use this to test if you'd like. I believe the standard compose configuration in the project should suffice too.

services:
  photoprism:
    image: photoprism/photoprism:develop
    build: .
    ports:
      - 8080:8080
    environment:
      PHOTOPRISM_ADMIN_USER: admin
      PHOTOPRISM_ADMIN_PASSWORD: admin
      PHOTOPRISM_DISABLE_PLACES: true
      PHOTOPRISM_DISABLE_FFMPEG: true
      PHOTOPRISM_DISABLE_DARKTABLE: true
      PHOTOPRISM_DISABLE_RAWTHERAPEE: true
      PHOTOPRISM_DISABLE_SIPS: true
      PHOTOPRISM_DISABLE_HEIFCONVERT: true
      PHOTOPRISM_DISABLE_TENSORFLOW: true
      PHOTOPRISM_DISABLE_FACES: true
      PHOTOPRISM_DETECT_NSFW: true
      PHOTOPRISM_UPLOAD_NSFW: true
      PHOTOPRISM_SITE_AUTHOR: PhotoPrism
      PHOTOPRISM_SITE_TITLE: PhotoPrism (local)
      PHOTOPRISM_HTTP_PORT: 8080
      PHOTOPRISM_HTTP_MODE: release
      PHOTOPRISM_HTTP_COMPRESSION: gzip
      PHOTOPRISM_IMPORT_PATH: /photoprism/storage/import
      PHOTOPRISM_ORIGINALS_PATH: /photoprism/storage/originals
      PHOTOPRISM_STORAGE_PATH: /photoprism/storage
      PHOTOPRISM_SIDECAR_PATH: /photoprism/storage/sidecar
      PHOTOPRISM_CACHE_PATH: /photoprism/storage/cache
      PHOTOPRISM_TEMP_PATH: /photoprism/storage/temp
      PHOTOPRISM_BACKUP_PATH: /photoprism/storage/backup
      PHOTOPRISM_DATABASE_DRIVER: sqlite
      PHOTOPRISM_DATABASE_DSN: /photoprism/storage/photoprism.db
      PHOTOPRISM_DATABASE_USER: photoprism-sqlite-user
      PHOTOPRISM_DATABASE_PASSWORD: admin
      PHOTOPRISM_APP_ICON: app
      PHOTOPRISM_SPONSOR: true
      PHOTOPRISM_DISABLE_TLS: true
    working_dir: "/go/src/github.com/photoprism/photoprism"
    volumes:
      - ".:/go/src/github.com/photoprism/photoprism"
      - "./storage:/photoprism"
      - "go-mod:/go/pkg/mod"

  prom:
    image: prom/prometheus:latest
    ports:
      - 9090:9090
    configs:
      - source: prometheus.yml
        target: /etc/prometheus/prometheus.yml
        mode: 0444

volumes:
  go-mod:
    driver: local

configs:
  prometheus.yml:
    content: |
      global:
        scrape_interval: 60s
        scrape_timeout: 10s
      scrape_configs:
        - job_name: "photoprism"
          metrics_path: "/api/v1/metrics"
          oauth2:
            client_id: "cs5cpu17n6gj2qo5"
            client_secret: "xcCbOrw6I0vcoXzhnOmXhjpVSyFq0l0e"
            token_url: "http://photoprism:8080/api/v1/oauth/token"
            scopes:
              - 'metrics'
            endpoint_params:
              grant_type: "client_credentials"
          static_configs:
            - targets: ["photoprism:8080"]

After this revision, the scrape succeeds.

The prometheus exposition format documentation can be found here.

Acceptance Criteria

  • New features or enhancements are fully implemented and do not break existing functionality, so that they can be released at any time without requiring additional work
  • Automated unit and/or acceptance tests are included to ensure that changes work as expected and to reduce repetitive manual work
  • Documentation has been / will be updated, especially as it relates to new configuration options or potentially disruptive changes
  • The user interface has been tested on Chrome, Safari, and Firefox and is fully responsive for use on phones, tablets, and desktop computers
  • Database-related changes have been successfully tested with SQLite 3 and MariaDB 10.5.12+

@lastzero

Copy link
Copy Markdown
Member

@brandon1024 Thank you very much! I apologize for the wait.

It would be great to use the SetContentType() function with the ContentTypeText constant for this:

Is it required to add a "version" like ; version=0.0.4? Why is it 0.0.4 and not e.g. 1.0.0?

@lastzero lastzero added needs-work Has problems that need to be resolved api Server API endpoints and documentation labels Jun 19, 2025
@lastzero lastzero moved this to Development 🐝 in Roadmap πŸš€βœ¨ Jun 19, 2025
@brandon1024

Copy link
Copy Markdown
Contributor Author

No worries at all @lastzero :-)

It would be great to use the SetContentType() function with the ContentTypeText constant for this

Can do! I'll make this change tonight and ping you when it's done.

Is it required to add a "version" like ; version=0.0.4?

According to the prometheus docs, the version can be omitted and it'll fall back to the latest text format version. I haven't tried that though, so a quick test would be a good idea. With that said, I took a look at what the official node exporter response looks like, and they set this header with a version:

< HTTP/1.1 200 OK
< Content-Type: text/plain; version=0.0.4; charset=utf-8; escaping=underscores
< Date: Fri, 20 Jun 2025 06:20:10 GMT
< Transfer-Encoding: chunked

The official promhttp library also sets this header and version, using the version constant defined as a constant here. We could just use that directly.

Why is it 0.0.4 and not e.g. 1.0.0?

0.0.4 is the latest prometheus text format version, so we shouldn't use 1.0.0.

@lastzero

lastzero commented Jun 20, 2025

Copy link
Copy Markdown
Member

I see. So, the version doesn't refer to the client library or our application version, which we could read dynamically? If so, you can add a suitable content type constant for this to the file linked above. πŸ‘Œ

@brandon1024

Copy link
Copy Markdown
Contributor Author

Genau, it's the version of the text format itself and not the client :) Can do! Thanks!

The prometheus text format requires metrics endpoints respond with the
content-type 'text/plain; version=0.0.4'. Without this, newer versions
of prometheus fail to scrape the metrics endpoint and report an error.
It's possible to work around this by setting the
'fallback_scrape_protocol' setting in the prometheus scrape target
configuration, but this revision sets the content type appropriately to
avoid this in the first place.

Add test for the content type in the response.
@brandon1024

Copy link
Copy Markdown
Contributor Author

@lastzero This should be ready to go now :-)

@lastzero

Copy link
Copy Markdown
Member

Thank you very much! ✨

@lastzero
lastzero merged commit 1d8fa4e into photoprism:develop Jun 23, 2025
@lastzero lastzero added merged Changes are merged, but may require further testing and removed needs-work Has problems that need to be resolved labels Jun 23, 2025
@lastzero lastzero moved this from Development 🐝 to Test πŸ•΅ in Roadmap πŸš€βœ¨ Jun 23, 2025
lastzero added a commit that referenced this pull request Jun 23, 2025
Signed-off-by: Michael Mayer <michael@photoprism.app>
@lastzero lastzero moved this from Test πŸ•΅ to Preview 🐳 in Roadmap πŸš€βœ¨ Jun 24, 2025
@lastzero lastzero moved this from Preview 🐳 to Release 🌈 in Roadmap πŸš€βœ¨ Jul 7, 2025
@lastzero lastzero moved this from Release 🌈 to Development 🐝 in Roadmap πŸš€βœ¨ Jul 7, 2025
@lastzero lastzero moved this from Development 🐝 to Release 🌈 in Roadmap πŸš€βœ¨ Jul 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api Server API endpoints and documentation merged Changes are merged, but may require further testing

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

2 participants