diff --git a/CHANGELOG.md b/CHANGELOG.md index 30ba22d1..269617f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,13 @@ To set a custom tile URL, go to the user settings and set the `Maps` section to - Safe settings for user with default values. - In the user settings, you can now set a custom tile URL for the map. #429 #715 +- If you have Prometheus exporter enabled, you can now see a `ruby_dawarich_map_tiles` metric in Prometheus, which shows the total number of map tiles loaded. Example: + +``` +# HELP ruby_dawarich_map_tiles +# TYPE ruby_dawarich_map_tiles gauge +ruby_dawarich_map_tiles 99 +``` # 0.24.0 - 2025-02-10 diff --git a/Procfile.prometheus.dev b/Procfile.prometheus.dev index 71fe0374..95a12639 100644 --- a/Procfile.prometheus.dev +++ b/Procfile.prometheus.dev @@ -1,2 +1,2 @@ prometheus_exporter: bundle exec prometheus_exporter -b ANY -web: bin/rails server -p 3000 -b :: \ No newline at end of file +web: bin/rails server -p 3000 -b :: diff --git a/README.md b/README.md index 0d21ed03..5aa76a1b 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ Donate using crypto: [0x6bAd13667692632f1bF926cA9B421bEe7EaEB8D4](https://ethers - Explore statistics like the number of countries and cities visited, total distance traveled, and more! 📄 **Changelog**: Find the latest updates [here](CHANGELOG.md). + 👩‍💻 **Contribute**: See [CONTRIBUTING.md](CONTRIBUTING.md) for how to contribute to Dawarich. --- diff --git a/app/controllers/api/v1/tile_usages_controller.rb b/app/controllers/api/v1/tile_usages_controller.rb new file mode 100644 index 00000000..d5d74d9d --- /dev/null +++ b/app/controllers/api/v1/tile_usages_controller.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class Api::V1::TileUsagesController < ApiController + def create + TileUsage::Track.new(params[:tile_count].to_i).call + + head :ok + end +end diff --git a/app/javascript/controllers/maps_controller.js b/app/javascript/controllers/maps_controller.js index f08af801..53b39c20 100644 --- a/app/javascript/controllers/maps_controller.js +++ b/app/javascript/controllers/maps_controller.js @@ -8,9 +8,7 @@ import { createMarkersArray } from "../maps/markers"; import { createPolylinesLayer, updatePolylinesOpacity, - updatePolylinesColors, - calculateSpeed, - getSpeedColor + updatePolylinesColors } from "../maps/polylines"; import { fetchAndDrawAreas, handleAreaCreated } from "../maps/areas"; @@ -32,9 +30,13 @@ import { countryCodesMap } from "../maps/country_codes"; import "leaflet-draw"; import { initializeFogCanvas, drawFogCanvas, createFogOverlay } from "../maps/fog_of_war"; +import { TileMonitor } from "../maps/tile_monitor"; export default class extends Controller { static targets = ["container"]; + static values = { + monitoringEnabled: Boolean + } settingsButtonAdded = false; layerControl = null; @@ -245,6 +247,19 @@ export default class extends Controller { if (this.liveMapEnabled) { this.setupSubscription(); } + + // Initialize tile monitor + this.tileMonitor = new TileMonitor(this.monitoringEnabledValue, this.apiKey); + + // Add tile load event handlers to each base layer + Object.entries(this.baseMaps()).forEach(([name, layer]) => { + layer.on('tileload', () => { + this.tileMonitor.recordTileLoad(name); + }); + }); + + // Start monitoring + this.tileMonitor.startMonitoring(); } disconnect() { @@ -260,6 +275,11 @@ export default class extends Controller { if (this.map) { this.map.remove(); } + + // Stop tile monitoring + if (this.tileMonitor) { + this.tileMonitor.stopMonitoring(); + } } setupSubscription() { diff --git a/app/javascript/maps/tile_monitor.js b/app/javascript/maps/tile_monitor.js new file mode 100644 index 00000000..bd5da516 --- /dev/null +++ b/app/javascript/maps/tile_monitor.js @@ -0,0 +1,67 @@ +export class TileMonitor { + constructor(monitoringEnabled, apiKey) { + this.monitoringEnabled = monitoringEnabled; + this.apiKey = apiKey; + this.tileQueue = 0; + this.tileUpdateInterval = null; + } + + startMonitoring() { + // Only start the interval if monitoring is enabled + if (!this.monitoringEnabled) return; + + // Clear any existing interval + if (this.tileUpdateInterval) { + clearInterval(this.tileUpdateInterval); + } + + // Set up a regular interval to send stats + this.tileUpdateInterval = setInterval(() => { + this.sendTileUsage(); + }, 5000); // Exactly every 5 seconds + } + + stopMonitoring() { + if (this.tileUpdateInterval) { + clearInterval(this.tileUpdateInterval); + this.sendTileUsage(); // Send any remaining stats + } + } + + recordTileLoad() { + if (!this.monitoringEnabled) return; + this.tileQueue += 1; + } + + sendTileUsage() { + // Don't send if monitoring is disabled or queue is empty + if (!this.monitoringEnabled || this.tileQueue === 0) return; + + const currentCount = this.tileQueue; + console.log('Sending tile usage batch:', currentCount); + + fetch('/api/v1/tile_usages', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${this.apiKey}` + }, + body: JSON.stringify({ + tile_count: currentCount + }) + }) + .then(response => { + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + // Only subtract sent count if it hasn't changed + if (this.tileQueue === currentCount) { + this.tileQueue = 0; + } else { + this.tileQueue -= currentCount; + } + console.log('Tile usage batch sent successfully'); + }) + .catch(error => console.error('Error recording tile usage:', error)); + } +} diff --git a/app/services/tile_usage/track.rb b/app/services/tile_usage/track.rb new file mode 100644 index 00000000..69d1b361 --- /dev/null +++ b/app/services/tile_usage/track.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class TileUsage::Track + def initialize(count = 1) + @count = count + end + + def call + metric_data = { + type: 'counter', + name: 'dawarich_map_tiles', + value: @count + } + + PrometheusExporter::Client.default.send_json(metric_data) + rescue StandardError => e + Rails.logger.error("Failed to send tile usage metric: #{e.message}") + end +end diff --git a/app/views/map/index.html.erb b/app/views/map/index.html.erb index 9fa4a0fe..511f12a7 100644 --- a/app/views/map/index.html.erb +++ b/app/views/map/index.html.erb @@ -53,6 +53,7 @@ data-coordinates="<%= @coordinates %>" data-distance="<%= @distance %>" data-points_number="<%= @points_number %>" + data-maps-monitoring-enabled-value="<%= DawarichSettings.prometheus_exporter_enabled? %>" data-timezone="<%= Rails.configuration.time_zone %>">
diff --git a/app/views/settings/maps/index.html.erb b/app/views/settings/maps/index.html.erb index b295028c..2e50950d 100644 --- a/app/views/settings/maps/index.html.erb +++ b/app/views/settings/maps/index.html.erb @@ -3,10 +3,25 @@
<%= render 'settings/navigation' %> -
+

Maps settings

+ +
<%= form_for :maps, url: settings_maps_path, diff --git a/config/initializers/03_dawarich_settings.rb b/config/initializers/03_dawarich_settings.rb index 451ed716..589d9ef1 100644 --- a/config/initializers/03_dawarich_settings.rb +++ b/config/initializers/03_dawarich_settings.rb @@ -18,12 +18,11 @@ class DawarichSettings @geoapify_enabled ||= GEOAPIFY_API_KEY.present? end - def meters_between_tracks - @meters_between_tracks ||= 300 - end - - def minutes_between_tracks - @minutes_between_tracks ||= 20 + def prometheus_exporter_enabled? + @prometheus_exporter_enabled ||= + ENV['PROMETHEUS_EXPORTER_ENABLED'].to_s == 'true' && + ENV['PROMETHEUS_EXPORTER_HOST'].present? && + ENV['PROMETHEUS_EXPORTER_PORT'].present? end end end diff --git a/config/initializers/prometheus.rb b/config/initializers/prometheus.rb index 3573fb84..1a2f38e0 100644 --- a/config/initializers/prometheus.rb +++ b/config/initializers/prometheus.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -if !Rails.env.test? && ENV['PROMETHEUS_EXPORTER_ENABLED'].to_s == 'true' +if !Rails.env.test? && DawarichSettings.prometheus_exporter_enabled? require 'prometheus_exporter/middleware' require 'prometheus_exporter/instrumentation' diff --git a/config/routes.rb b/config/routes.rb index c3ef1717..45b55576 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -96,6 +96,8 @@ Rails.application.routes.draw do get 'thumbnail', constraints: { id: %r{[^/]+} } end end + + resources :tile_usages, only: [:create] end end end