From f9b7af64550be1320acc84d60184e8c8ce873b94 Mon Sep 17 00:00:00 2001 From: Ariel Costas Guerrero Date: Sun, 25 Jan 2026 21:05:33 +0100 Subject: feat: Add OpenTelemetry instrumentation and configuration for enhanced telemetry tracking --- src/Enmarcha.Backend/Services/ArrivalsPipeline.cs | 2 ++ .../Services/Geocoding/GeoapifyGeocodingService.cs | 20 ++++++++++++++++++-- .../Services/Processors/CorunaRealTimeProcessor.cs | 5 +++-- .../Services/Processors/SantiagoRealTimeProcessor.cs | 7 ++++--- .../Services/Processors/VitrasaRealTimeProcessor.cs | 6 ++++-- 5 files changed, 31 insertions(+), 9 deletions(-) (limited to 'src/Enmarcha.Backend/Services') diff --git a/src/Enmarcha.Backend/Services/ArrivalsPipeline.cs b/src/Enmarcha.Backend/Services/ArrivalsPipeline.cs index 57a46e1..4f49afe 100644 --- a/src/Enmarcha.Backend/Services/ArrivalsPipeline.cs +++ b/src/Enmarcha.Backend/Services/ArrivalsPipeline.cs @@ -53,8 +53,10 @@ public class ArrivalsPipeline /// public async Task ExecuteAsync(ArrivalsContext context) { + using var activity = Telemetry.Source.StartActivity("ArrivalsPipeline"); foreach (var processor in _processors) { + using var processorActivity = Telemetry.Source.StartActivity($"Processor:{processor.GetType().Name}"); await processor.ProcessAsync(context); } } diff --git a/src/Enmarcha.Backend/Services/Geocoding/GeoapifyGeocodingService.cs b/src/Enmarcha.Backend/Services/Geocoding/GeoapifyGeocodingService.cs index d6cf5f6..86386e8 100644 --- a/src/Enmarcha.Backend/Services/Geocoding/GeoapifyGeocodingService.cs +++ b/src/Enmarcha.Backend/Services/Geocoding/GeoapifyGeocodingService.cs @@ -32,13 +32,19 @@ public class GeoapifyGeocodingService : IGeocodingService public async Task> GetAutocompleteAsync(string query) { + using var activity = Telemetry.Source.StartActivity("GeoapifyAutocomplete"); + activity?.SetTag("query", query); + if (string.IsNullOrWhiteSpace(query)) { return []; } var cacheKey = $"nominatim_autocomplete_{query.ToLowerInvariant()}"; - if (_cache.TryGetValue(cacheKey, out List? cachedResults) && cachedResults != null) + var cacheHit = _cache.TryGetValue(cacheKey, out List? cachedResults); + activity?.SetTag("cache.hit", cacheHit); + + if (cacheHit && cachedResults != null) { return cachedResults; } @@ -55,11 +61,13 @@ public class GeoapifyGeocodingService : IGeocodingService .Select(MapToPlannerSearchResult) .ToList() ?? []; + activity?.SetTag("results.count", results.Count); _cache.Set(cacheKey, results, TimeSpan.FromMinutes(60)); return results; } catch (Exception ex) { + activity?.SetStatus(System.Diagnostics.ActivityStatusCode.Error, ex.Message); _logger.LogError(ex, "Error fetching Geoapify autocomplete results from {Url}", url); return new List(); } @@ -67,8 +75,15 @@ public class GeoapifyGeocodingService : IGeocodingService public async Task GetReverseGeocodeAsync(double lat, double lon) { + using var activity = Telemetry.Source.StartActivity("GeoapifyReverseGeocode"); + activity?.SetTag("lat", lat); + activity?.SetTag("lon", lon); + var cacheKey = $"nominatim_reverse_{lat:F5}_{lon:F5}"; - if (_cache.TryGetValue(cacheKey, out PlannerSearchResult? cachedResult) && cachedResult != null) + var cacheHit = _cache.TryGetValue(cacheKey, out PlannerSearchResult? cachedResult); + activity?.SetTag("cache.hit", cacheHit); + + if (cacheHit && cachedResult != null) { return cachedResult; } @@ -88,6 +103,7 @@ public class GeoapifyGeocodingService : IGeocodingService } catch (Exception ex) { + activity?.SetStatus(System.Diagnostics.ActivityStatusCode.Error, ex.Message); _logger.LogError(ex, "Error fetching Geoapify reverse geocode results from {Url}", url); return null; } diff --git a/src/Enmarcha.Backend/Services/Processors/CorunaRealTimeProcessor.cs b/src/Enmarcha.Backend/Services/Processors/CorunaRealTimeProcessor.cs index 6a9d9dc..b933bf4 100644 --- a/src/Enmarcha.Backend/Services/Processors/CorunaRealTimeProcessor.cs +++ b/src/Enmarcha.Backend/Services/Processors/CorunaRealTimeProcessor.cs @@ -13,12 +13,12 @@ public class CorunaRealTimeProcessor : AbstractRealTimeProcessor private readonly ShapeTraversalService _shapeService; public CorunaRealTimeProcessor( - HttpClient http, + CorunaRealtimeEstimatesProvider realtime, FeedService feedService, ILogger logger, ShapeTraversalService shapeService) { - _realtime = new CorunaRealtimeEstimatesProvider(http); + _realtime = realtime; _feedService = feedService; _logger = logger; _shapeService = shapeService; @@ -41,6 +41,7 @@ public class CorunaRealTimeProcessor : AbstractRealTimeProcessor } var realtime = await _realtime.GetEstimatesForStop(numericStopId); + System.Diagnostics.Activity.Current?.SetTag("realtime.count", realtime.Count); var usedTripIds = new HashSet(); diff --git a/src/Enmarcha.Backend/Services/Processors/SantiagoRealTimeProcessor.cs b/src/Enmarcha.Backend/Services/Processors/SantiagoRealTimeProcessor.cs index 929e439..a4f7d5b 100644 --- a/src/Enmarcha.Backend/Services/Processors/SantiagoRealTimeProcessor.cs +++ b/src/Enmarcha.Backend/Services/Processors/SantiagoRealTimeProcessor.cs @@ -12,11 +12,11 @@ public class SantiagoRealTimeProcessor : AbstractRealTimeProcessor private readonly ILogger _logger; public SantiagoRealTimeProcessor( - HttpClient http, + SantiagoRealtimeEstimatesProvider realtime, FeedService feedService, ILogger logger) { - _realtime = new SantiagoRealtimeEstimatesProvider(http); + _realtime = realtime; _feedService = feedService; _logger = logger; } @@ -31,6 +31,7 @@ public class SantiagoRealTimeProcessor : AbstractRealTimeProcessor try { var realtime = await _realtime.GetEstimatesForStop(numericStopId); + System.Diagnostics.Activity.Current?.SetTag("realtime.count", realtime.Count); var usedTripIds = new HashSet(); @@ -46,7 +47,7 @@ public class SantiagoRealTimeProcessor : AbstractRealTimeProcessor RouteMatch = true }) .Where(x => x.RouteMatch) // Strict route matching - .Where(x => x.TimeDiff is >= -5 and <= 25) // Allow 2m early (RealTime < Schedule) or 25m late (RealTime > Schedule) + .Where(x => x.TimeDiff is >= -5 and <= 35) // Allow 2m early (RealTime < Schedule) or 25m late (RealTime > Schedule) .OrderBy(x => Math.Abs(x.TimeDiff)) // Best time fit .FirstOrDefault(); diff --git a/src/Enmarcha.Backend/Services/Processors/VitrasaRealTimeProcessor.cs b/src/Enmarcha.Backend/Services/Processors/VitrasaRealTimeProcessor.cs index 5d44995..0ce54c2 100644 --- a/src/Enmarcha.Backend/Services/Processors/VitrasaRealTimeProcessor.cs +++ b/src/Enmarcha.Backend/Services/Processors/VitrasaRealTimeProcessor.cs @@ -16,13 +16,13 @@ public class VitrasaRealTimeProcessor : AbstractRealTimeProcessor private readonly AppConfiguration _configuration; public VitrasaRealTimeProcessor( - HttpClient http, + VigoTransitApiClient api, FeedService feedService, ILogger logger, ShapeTraversalService shapeService, IOptions options) { - _api = new VigoTransitApiClient(http); + _api = api; _feedService = feedService; _logger = logger; _shapeService = shapeService; @@ -52,6 +52,8 @@ public class VitrasaRealTimeProcessor : AbstractRealTimeProcessor .Where(e => !string.IsNullOrWhiteSpace(e.Route) && !e.Route.Trim().EndsWith('*')) .ToList(); + System.Diagnostics.Activity.Current?.SetTag("realtime.count", estimates.Count); + var usedTripIds = new HashSet(); var newArrivals = new List(); -- cgit v1.3