More Logging
This commit is contained in:
parent
fa5c04377c
commit
73dc2e1c15
1 changed files with 143 additions and 41 deletions
|
|
@ -52,20 +52,24 @@ public class WeatherService {
|
|||
@Transactional
|
||||
public Optional<WeatherData> fetchWeatherForActivity(Activity activity) {
|
||||
log.info("=== Weather fetch requested for activity {} ===", activity.getId());
|
||||
log.info("Weather enabled: {}, API key configured: {}", weatherEnabled, (apiKey != null && !apiKey.isBlank()));
|
||||
log.info("Weather configuration: enabled={}, API key configured={}, API key length={}",
|
||||
weatherEnabled, (apiKey != null && !apiKey.isBlank()),
|
||||
(apiKey != null ? apiKey.length() : 0));
|
||||
|
||||
if (!weatherEnabled) {
|
||||
log.warn("Weather fetching is DISABLED in configuration (fitpub.weather.enabled=false)");
|
||||
log.warn("Weather fetching is DISABLED in configuration (fitpub.weather.enabled=false). " +
|
||||
"Set fitpub.weather.enabled=true in application properties to enable.");
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
if (apiKey == null || apiKey.isBlank()) {
|
||||
log.error("Weather API key is NOT CONFIGURED (fitpub.weather.api-key is empty)");
|
||||
log.error("Weather API key is NOT CONFIGURED (fitpub.weather.api-key is empty). " +
|
||||
"Please set fitpub.weather.api-key in application properties.");
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
log.debug("Weather API key present (length: {} chars, starts with: {}...)",
|
||||
apiKey.length(), apiKey.length() > 4 ? apiKey.substring(0, 4) : "???");
|
||||
log.info("Weather API key present: length={} chars, first 4 chars={}...",
|
||||
apiKey.length(), apiKey.length() > 4 ? apiKey.substring(0, 4) : "???");
|
||||
|
||||
// Check if weather data already exists
|
||||
if (weatherDataRepository.existsByActivityId(activity.getId())) {
|
||||
|
|
@ -93,19 +97,18 @@ public class WeatherService {
|
|||
}
|
||||
|
||||
JsonNode firstPoint = trackPoints.get(0);
|
||||
|
||||
log.debug("First track point missing lat/lon for activity {}", activity.getId());
|
||||
log.debug("First track point: {}", firstPoint.toString());
|
||||
|
||||
// Check if lat/lon fields exist
|
||||
if (!firstPoint.has("lat") || !firstPoint.has("lon")) {
|
||||
log.error("First track point missing lat/lon fields for activity {}",
|
||||
activity.getId());
|
||||
log.error("First track point MISSING lat/lon fields for activity {}. Available fields: {}",
|
||||
activity.getId(), firstPoint.fieldNames());
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
double lat = firstPoint.get("lat").asDouble();
|
||||
double lon = firstPoint.get("lon").asDouble();
|
||||
log.info("Extracted location: lat={}, lon={}", lat, lon);
|
||||
log.info("Extracted location from first track point: lat={}, lon={}", lat, lon);
|
||||
|
||||
// Check if activity is recent (within 5 days) - use current weather API
|
||||
// Otherwise use historical data API (requires paid plan)
|
||||
|
|
@ -117,20 +120,25 @@ public class WeatherService {
|
|||
|
||||
WeatherData weatherData;
|
||||
if (daysDifference <= 5) {
|
||||
log.info("Activity is recent (within 5 days), fetching current weather");
|
||||
log.info("Activity is RECENT ({} days old, within 5 day threshold), fetching current weather from OpenWeatherMap", daysDifference);
|
||||
weatherData = fetchCurrentWeather(lat, lon, activity.getId());
|
||||
} else {
|
||||
log.warn("Activity is {} days old (>5 days), historical weather data requires paid API plan. Skipping.", daysDifference);
|
||||
log.warn("Activity is {} days old (exceeds 5 day threshold). Historical weather data requires OpenWeatherMap paid API plan. Skipping weather fetch.", daysDifference);
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
if (weatherData != null) {
|
||||
log.info("Successfully fetched and parsed weather data, saving to database");
|
||||
WeatherData saved = weatherDataRepository.save(weatherData);
|
||||
log.info("Weather data saved with ID: {}", saved.getId());
|
||||
return Optional.of(saved);
|
||||
log.info("Successfully fetched and parsed weather data. Attempting to save to database...");
|
||||
try {
|
||||
WeatherData saved = weatherDataRepository.save(weatherData);
|
||||
log.info("Weather data SUCCESSFULLY SAVED to database with ID: {}", saved.getId());
|
||||
return Optional.of(saved);
|
||||
} catch (Exception e) {
|
||||
log.error("FAILED to save weather data to database: {}", e.getMessage(), e);
|
||||
return Optional.empty();
|
||||
}
|
||||
} else {
|
||||
log.error("Weather data fetch returned null");
|
||||
log.error("Weather data fetch returned NULL - check API errors above");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
|
|
@ -145,54 +153,93 @@ public class WeatherService {
|
|||
* Fetch current weather data from OpenWeatherMap.
|
||||
*/
|
||||
private WeatherData fetchCurrentWeather(double lat, double lon, UUID activityId) {
|
||||
log.info("=== fetchCurrentWeather START === activityId={}, lat={}, lon={}", activityId, lat, lon);
|
||||
try {
|
||||
String url = String.format("%s?lat=%f&lon=%f&appid=%s&units=metric",
|
||||
OPENWEATHERMAP_API_URL, lat, lon, apiKey);
|
||||
|
||||
String maskedUrl = url.replace(apiKey, "***API_KEY***");
|
||||
log.info("Making API request to OpenWeatherMap: {}", maskedUrl);
|
||||
log.debug("API URL: {}", OPENWEATHERMAP_API_URL);
|
||||
log.debug("Coordinates: lat={}, lon={}", lat, lon);
|
||||
log.info("Constructed OpenWeatherMap API URL: {}", maskedUrl);
|
||||
log.info("Request parameters: lat={}, lon={}, units=metric", lat, lon);
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
log.info("Sending HTTP GET request to OpenWeatherMap...");
|
||||
String response = restTemplate.getForObject(URI.create(url), String.class);
|
||||
long duration = System.currentTimeMillis() - startTime;
|
||||
|
||||
log.info("API request completed in {}ms", duration);
|
||||
log.info("HTTP request completed in {}ms, response received", duration);
|
||||
|
||||
if (response == null) {
|
||||
log.error("API response is NULL - no data returned from OpenWeatherMap");
|
||||
log.error("API response is NULL - RestTemplate returned null, no data from OpenWeatherMap");
|
||||
return null;
|
||||
}
|
||||
|
||||
log.debug("API response length: {} chars", response.length());
|
||||
log.debug("API response preview: {}", response.length() > 200 ? response.substring(0, 200) + "..." : response);
|
||||
log.info("API response received: {} characters", response.length());
|
||||
log.info("API response (first 300 chars): {}",
|
||||
response.length() > 300 ? response.substring(0, 300) + "..." : response);
|
||||
|
||||
log.info("Parsing weather response JSON...");
|
||||
WeatherData weatherData = parseWeatherResponse(response, activityId);
|
||||
|
||||
if (weatherData == null) {
|
||||
log.error("Failed to parse weather response");
|
||||
log.error("FAILED to parse weather response - see parsing errors above");
|
||||
} else {
|
||||
log.info("Successfully parsed weather data: temp={}°C, condition={}",
|
||||
weatherData.getTemperatureCelsius(), weatherData.getWeatherCondition());
|
||||
log.info("Successfully parsed weather data: temp={}°C, feels_like={}°C, condition='{}', description='{}', humidity={}%, pressure={} hPa, wind={} m/s",
|
||||
weatherData.getTemperatureCelsius(),
|
||||
weatherData.getFeelsLikeCelsius(),
|
||||
weatherData.getWeatherCondition(),
|
||||
weatherData.getWeatherDescription(),
|
||||
weatherData.getHumidity(),
|
||||
weatherData.getPressure(),
|
||||
weatherData.getWindSpeedMps());
|
||||
}
|
||||
|
||||
log.info("=== fetchCurrentWeather END === success={}", (weatherData != null));
|
||||
return weatherData;
|
||||
|
||||
} catch (org.springframework.web.client.HttpClientErrorException e) {
|
||||
log.error("HTTP CLIENT ERROR from OpenWeatherMap API: Status={}, Body={}",
|
||||
e.getStatusCode(), e.getResponseBodyAsString(), e);
|
||||
log.error("=== HTTP CLIENT ERROR (4xx) from OpenWeatherMap API ===");
|
||||
log.error("Status Code: {}", e.getStatusCode());
|
||||
log.error("Status Text: {}", e.getStatusText());
|
||||
log.error("Response Body: {}", e.getResponseBodyAsString());
|
||||
log.error("Request URL (masked): {}", OPENWEATHERMAP_API_URL + "?lat=" + lat + "&lon=" + lon + "&appid=***&units=metric");
|
||||
if (e.getStatusCode().value() == 401) {
|
||||
log.error("AUTHENTICATION FAILED - Check your OpenWeatherMap API key is valid and active");
|
||||
} else if (e.getStatusCode().value() == 404) {
|
||||
log.error("API ENDPOINT NOT FOUND - Check coordinates are valid: lat={}, lon={}", lat, lon);
|
||||
} else if (e.getStatusCode().value() == 429) {
|
||||
log.error("RATE LIMIT EXCEEDED - Too many API requests. Check your OpenWeatherMap plan limits.");
|
||||
}
|
||||
log.error("Exception details:", e);
|
||||
return null;
|
||||
} catch (org.springframework.web.client.HttpServerErrorException e) {
|
||||
log.error("HTTP SERVER ERROR from OpenWeatherMap API: Status={}, Body={}",
|
||||
e.getStatusCode(), e.getResponseBodyAsString(), e);
|
||||
log.error("=== HTTP SERVER ERROR (5xx) from OpenWeatherMap API ===");
|
||||
log.error("Status Code: {}", e.getStatusCode());
|
||||
log.error("Status Text: {}", e.getStatusText());
|
||||
log.error("Response Body: {}", e.getResponseBodyAsString());
|
||||
log.error("OpenWeatherMap service may be experiencing issues. Try again later.");
|
||||
log.error("Exception details:", e);
|
||||
return null;
|
||||
} catch (org.springframework.web.client.ResourceAccessException e) {
|
||||
log.error("=== NETWORK/CONNECTION ERROR accessing OpenWeatherMap API ===");
|
||||
log.error("Error message: {}", e.getMessage());
|
||||
log.error("This could indicate: DNS resolution failure, network connectivity issues, firewall blocking, or SSL certificate problems");
|
||||
log.error("API URL attempted: {}", OPENWEATHERMAP_API_URL);
|
||||
log.error("Exception details:", e);
|
||||
return null;
|
||||
} catch (org.springframework.web.client.RestClientException e) {
|
||||
log.error("REST CLIENT EXCEPTION calling OpenWeatherMap API: {}", e.getMessage(), e);
|
||||
log.error("=== REST CLIENT EXCEPTION calling OpenWeatherMap API ===");
|
||||
log.error("Exception type: {}", e.getClass().getName());
|
||||
log.error("Error message: {}", e.getMessage());
|
||||
log.error("Exception details:", e);
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
log.error("UNEXPECTED EXCEPTION fetching current weather: {} - {}",
|
||||
e.getClass().getSimpleName(), e.getMessage(), e);
|
||||
log.error("=== UNEXPECTED EXCEPTION fetching current weather ===");
|
||||
log.error("Exception type: {}", e.getClass().getName());
|
||||
log.error("Error message: {}", e.getMessage());
|
||||
log.error("Activity ID: {}", activityId);
|
||||
log.error("Coordinates: lat={}, lon={}", lat, lon);
|
||||
log.error("Full stack trace:", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -201,8 +248,10 @@ public class WeatherService {
|
|||
* Parse OpenWeatherMap API response and create WeatherData entity.
|
||||
*/
|
||||
private WeatherData parseWeatherResponse(String response, UUID activityId) {
|
||||
log.debug("=== parseWeatherResponse START === activityId={}", activityId);
|
||||
try {
|
||||
JsonNode root = objectMapper.readTree(response);
|
||||
log.debug("JSON parsed successfully, root node present: {}", root != null);
|
||||
|
||||
WeatherData weatherData = new WeatherData();
|
||||
weatherData.setActivityId(activityId);
|
||||
|
|
@ -210,35 +259,54 @@ public class WeatherService {
|
|||
// Main temperature data
|
||||
if (root.has("main")) {
|
||||
JsonNode main = root.get("main");
|
||||
log.debug("Parsing 'main' section: {}", main);
|
||||
weatherData.setTemperatureCelsius(getBigDecimal(main, "temp"));
|
||||
weatherData.setFeelsLikeCelsius(getBigDecimal(main, "feels_like"));
|
||||
weatherData.setHumidity(getInteger(main, "humidity"));
|
||||
weatherData.setPressure(getInteger(main, "pressure"));
|
||||
log.debug("Extracted main data: temp={}, feels_like={}, humidity={}, pressure={}",
|
||||
weatherData.getTemperatureCelsius(), weatherData.getFeelsLikeCelsius(),
|
||||
weatherData.getHumidity(), weatherData.getPressure());
|
||||
} else {
|
||||
log.warn("Response JSON does not contain 'main' section");
|
||||
}
|
||||
|
||||
// Wind data
|
||||
if (root.has("wind")) {
|
||||
JsonNode wind = root.get("wind");
|
||||
log.debug("Parsing 'wind' section: {}", wind);
|
||||
weatherData.setWindSpeedMps(getBigDecimal(wind, "speed"));
|
||||
weatherData.setWindDirection(getInteger(wind, "deg"));
|
||||
log.debug("Extracted wind data: speed={} m/s, direction={} degrees",
|
||||
weatherData.getWindSpeedMps(), weatherData.getWindDirection());
|
||||
} else {
|
||||
log.debug("Response JSON does not contain 'wind' section");
|
||||
}
|
||||
|
||||
// Weather condition
|
||||
if (root.has("weather") && root.get("weather").isArray() && !root.get("weather").isEmpty()) {
|
||||
JsonNode weather = root.get("weather").get(0);
|
||||
log.debug("Parsing 'weather' array (first element): {}", weather);
|
||||
weatherData.setWeatherCondition(getString(weather, "main"));
|
||||
weatherData.setWeatherDescription(getString(weather, "description"));
|
||||
weatherData.setWeatherIcon(getString(weather, "icon"));
|
||||
log.debug("Extracted weather condition: main='{}', description='{}', icon='{}'",
|
||||
weatherData.getWeatherCondition(), weatherData.getWeatherDescription(),
|
||||
weatherData.getWeatherIcon());
|
||||
} else {
|
||||
log.warn("Response JSON does not contain valid 'weather' array");
|
||||
}
|
||||
|
||||
// Clouds
|
||||
if (root.has("clouds")) {
|
||||
weatherData.setCloudiness(getInteger(root.get("clouds"), "all"));
|
||||
log.debug("Extracted cloudiness: {}%", weatherData.getCloudiness());
|
||||
}
|
||||
|
||||
// Visibility
|
||||
if (root.has("visibility")) {
|
||||
weatherData.setVisibilityMeters(root.get("visibility").asInt());
|
||||
log.debug("Extracted visibility: {} meters", weatherData.getVisibilityMeters());
|
||||
}
|
||||
|
||||
// Rain
|
||||
|
|
@ -246,6 +314,7 @@ public class WeatherService {
|
|||
JsonNode rain = root.get("rain");
|
||||
if (rain.has("1h")) {
|
||||
weatherData.setPrecipitationMm(BigDecimal.valueOf(rain.get("1h").asDouble()));
|
||||
log.debug("Extracted rain: {} mm/h", weatherData.getPrecipitationMm());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -254,6 +323,7 @@ public class WeatherService {
|
|||
JsonNode snow = root.get("snow");
|
||||
if (snow.has("1h")) {
|
||||
weatherData.setSnowMm(BigDecimal.valueOf(snow.get("1h").asDouble()));
|
||||
log.debug("Extracted snow: {} mm/h", weatherData.getSnowMm());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -263,20 +333,34 @@ public class WeatherService {
|
|||
if (sys.has("sunrise")) {
|
||||
weatherData.setSunrise(LocalDateTime.ofInstant(
|
||||
Instant.ofEpochSecond(sys.get("sunrise").asLong()), ZoneId.systemDefault()));
|
||||
log.debug("Extracted sunrise: {}", weatherData.getSunrise());
|
||||
}
|
||||
if (sys.has("sunset")) {
|
||||
weatherData.setSunset(LocalDateTime.ofInstant(
|
||||
Instant.ofEpochSecond(sys.get("sunset").asLong()), ZoneId.systemDefault()));
|
||||
log.debug("Extracted sunset: {}", weatherData.getSunset());
|
||||
}
|
||||
}
|
||||
|
||||
weatherData.setFetchedAt(LocalDateTime.now());
|
||||
weatherData.setDataSource("openweathermap");
|
||||
|
||||
log.info("Successfully parsed complete weather data");
|
||||
log.debug("=== parseWeatherResponse END === success=true");
|
||||
return weatherData;
|
||||
|
||||
} catch (com.fasterxml.jackson.core.JsonProcessingException e) {
|
||||
log.error("=== JSON PARSING ERROR ===");
|
||||
log.error("Failed to parse weather response as JSON");
|
||||
log.error("Response content: {}", response);
|
||||
log.error("Parse error: {}", e.getMessage(), e);
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
log.error("Error parsing weather response: {}", e.getMessage());
|
||||
log.error("=== UNEXPECTED ERROR parsing weather response ===");
|
||||
log.error("Exception type: {}", e.getClass().getName());
|
||||
log.error("Error message: {}", e.getMessage());
|
||||
log.error("Response content: {}", response);
|
||||
log.error("Full stack trace:", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -303,23 +387,41 @@ public class WeatherService {
|
|||
|
||||
// Helper methods to safely extract values from JSON
|
||||
private BigDecimal getBigDecimal(JsonNode node, String field) {
|
||||
if (node.has(field)) {
|
||||
return BigDecimal.valueOf(node.get(field).asDouble());
|
||||
if (node != null && node.has(field) && !node.get(field).isNull()) {
|
||||
try {
|
||||
return BigDecimal.valueOf(node.get(field).asDouble());
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to extract BigDecimal from field '{}': {}", field, e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
log.debug("Field '{}' not found or is null in node", field);
|
||||
return null;
|
||||
}
|
||||
|
||||
private Integer getInteger(JsonNode node, String field) {
|
||||
if (node.has(field)) {
|
||||
return node.get(field).asInt();
|
||||
if (node != null && node.has(field) && !node.get(field).isNull()) {
|
||||
try {
|
||||
return node.get(field).asInt();
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to extract Integer from field '{}': {}", field, e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
log.debug("Field '{}' not found or is null in node", field);
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getString(JsonNode node, String field) {
|
||||
if (node.has(field)) {
|
||||
return node.get(field).asText();
|
||||
if (node != null && node.has(field) && !node.get(field).isNull()) {
|
||||
try {
|
||||
return node.get(field).asText();
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to extract String from field '{}': {}", field, e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
log.debug("Field '{}' not found or is null in node", field);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue