mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-09 08:47:11 -05:00
Export map tiles usage to Prometheus
This commit is contained in:
parent
d2d6f95322
commit
1580fb8ade
12 changed files with 152 additions and 12 deletions
|
|
@ -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.
|
- Safe settings for user with default values.
|
||||||
- In the user settings, you can now set a custom tile URL for the map. #429 #715
|
- 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
|
# 0.24.0 - 2025-02-10
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,2 @@
|
||||||
prometheus_exporter: bundle exec prometheus_exporter -b ANY
|
prometheus_exporter: bundle exec prometheus_exporter -b ANY
|
||||||
web: bin/rails server -p 3000 -b ::
|
web: bin/rails server -p 3000 -b ::
|
||||||
|
|
|
||||||
|
|
@ -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!
|
- Explore statistics like the number of countries and cities visited, total distance traveled, and more!
|
||||||
|
|
||||||
📄 **Changelog**: Find the latest updates [here](CHANGELOG.md).
|
📄 **Changelog**: Find the latest updates [here](CHANGELOG.md).
|
||||||
|
|
||||||
👩💻 **Contribute**: See [CONTRIBUTING.md](CONTRIBUTING.md) for how to contribute to Dawarich.
|
👩💻 **Contribute**: See [CONTRIBUTING.md](CONTRIBUTING.md) for how to contribute to Dawarich.
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
9
app/controllers/api/v1/tile_usages_controller.rb
Normal file
9
app/controllers/api/v1/tile_usages_controller.rb
Normal file
|
|
@ -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
|
||||||
|
|
@ -8,9 +8,7 @@ import { createMarkersArray } from "../maps/markers";
|
||||||
import {
|
import {
|
||||||
createPolylinesLayer,
|
createPolylinesLayer,
|
||||||
updatePolylinesOpacity,
|
updatePolylinesOpacity,
|
||||||
updatePolylinesColors,
|
updatePolylinesColors
|
||||||
calculateSpeed,
|
|
||||||
getSpeedColor
|
|
||||||
} from "../maps/polylines";
|
} from "../maps/polylines";
|
||||||
|
|
||||||
import { fetchAndDrawAreas, handleAreaCreated } from "../maps/areas";
|
import { fetchAndDrawAreas, handleAreaCreated } from "../maps/areas";
|
||||||
|
|
@ -32,9 +30,13 @@ import { countryCodesMap } from "../maps/country_codes";
|
||||||
|
|
||||||
import "leaflet-draw";
|
import "leaflet-draw";
|
||||||
import { initializeFogCanvas, drawFogCanvas, createFogOverlay } from "../maps/fog_of_war";
|
import { initializeFogCanvas, drawFogCanvas, createFogOverlay } from "../maps/fog_of_war";
|
||||||
|
import { TileMonitor } from "../maps/tile_monitor";
|
||||||
|
|
||||||
export default class extends Controller {
|
export default class extends Controller {
|
||||||
static targets = ["container"];
|
static targets = ["container"];
|
||||||
|
static values = {
|
||||||
|
monitoringEnabled: Boolean
|
||||||
|
}
|
||||||
|
|
||||||
settingsButtonAdded = false;
|
settingsButtonAdded = false;
|
||||||
layerControl = null;
|
layerControl = null;
|
||||||
|
|
@ -245,6 +247,19 @@ export default class extends Controller {
|
||||||
if (this.liveMapEnabled) {
|
if (this.liveMapEnabled) {
|
||||||
this.setupSubscription();
|
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() {
|
disconnect() {
|
||||||
|
|
@ -260,6 +275,11 @@ export default class extends Controller {
|
||||||
if (this.map) {
|
if (this.map) {
|
||||||
this.map.remove();
|
this.map.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stop tile monitoring
|
||||||
|
if (this.tileMonitor) {
|
||||||
|
this.tileMonitor.stopMonitoring();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setupSubscription() {
|
setupSubscription() {
|
||||||
|
|
|
||||||
67
app/javascript/maps/tile_monitor.js
Normal file
67
app/javascript/maps/tile_monitor.js
Normal file
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
19
app/services/tile_usage/track.rb
Normal file
19
app/services/tile_usage/track.rb
Normal file
|
|
@ -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
|
||||||
|
|
@ -53,6 +53,7 @@
|
||||||
data-coordinates="<%= @coordinates %>"
|
data-coordinates="<%= @coordinates %>"
|
||||||
data-distance="<%= @distance %>"
|
data-distance="<%= @distance %>"
|
||||||
data-points_number="<%= @points_number %>"
|
data-points_number="<%= @points_number %>"
|
||||||
|
data-maps-monitoring-enabled-value="<%= DawarichSettings.prometheus_exporter_enabled? %>"
|
||||||
data-timezone="<%= Rails.configuration.time_zone %>">
|
data-timezone="<%= Rails.configuration.time_zone %>">
|
||||||
<div data-maps-target="container" class="h-[25rem] rounded-lg w-full min-h-screen">
|
<div data-maps-target="container" class="h-[25rem] rounded-lg w-full min-h-screen">
|
||||||
<div id="fog" class="fog"></div>
|
<div id="fog" class="fog"></div>
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,25 @@
|
||||||
<div class="min-h-content w-full my-5">
|
<div class="min-h-content w-full my-5">
|
||||||
<%= render 'settings/navigation' %>
|
<%= render 'settings/navigation' %>
|
||||||
|
|
||||||
<div class="flex justify-between items-center mt-5">
|
<div class="flex justify-between items-center my-5">
|
||||||
<h1 class="font-bold text-4xl">Maps settings</h1>
|
<h1 class="font-bold text-4xl">Maps settings</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div role="alert" class="alert alert-info">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
class="h-6 w-6 shrink-0 stroke-current">
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
||||||
|
</svg>
|
||||||
|
<span>Please remember, that using a custom tile URL may result in extra costs. Check your map tile provider's terms of service for more information.</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-5" data-controller="map-preview">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-5" data-controller="map-preview">
|
||||||
<%= form_for :maps,
|
<%= form_for :maps,
|
||||||
url: settings_maps_path,
|
url: settings_maps_path,
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,11 @@ class DawarichSettings
|
||||||
@geoapify_enabled ||= GEOAPIFY_API_KEY.present?
|
@geoapify_enabled ||= GEOAPIFY_API_KEY.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
def meters_between_tracks
|
def prometheus_exporter_enabled?
|
||||||
@meters_between_tracks ||= 300
|
@prometheus_exporter_enabled ||=
|
||||||
end
|
ENV['PROMETHEUS_EXPORTER_ENABLED'].to_s == 'true' &&
|
||||||
|
ENV['PROMETHEUS_EXPORTER_HOST'].present? &&
|
||||||
def minutes_between_tracks
|
ENV['PROMETHEUS_EXPORTER_PORT'].present?
|
||||||
@minutes_between_tracks ||= 20
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# 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/middleware'
|
||||||
require 'prometheus_exporter/instrumentation'
|
require 'prometheus_exporter/instrumentation'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,8 @@ Rails.application.routes.draw do
|
||||||
get 'thumbnail', constraints: { id: %r{[^/]+} }
|
get 'thumbnail', constraints: { id: %r{[^/]+} }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
resources :tile_usages, only: [:create]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue