Skip to content

Add logging to IncentiveServiceImpl methods#235

Open
Kunxal wants to merge 2 commits intoPSMRI:mainfrom
Kunxal:main
Open

Add logging to IncentiveServiceImpl methods#235
Kunxal wants to merge 2 commits intoPSMRI:mainfrom
Kunxal:main

Conversation

@Kunxal
Copy link
Copy Markdown

@Kunxal Kunxal commented Apr 3, 2026

String Concatenation Logic Error in IncentiveServiceImpl

Description

There is a critical bug in the saveIncentivesMaster method of IncentiveServiceImpl.java where String.concat() is incorrectly used within a forEach loop. Due to Java's String immutability, the concatenation result is discarded, causing the method to always return an empty string for the saved records summary.

Location

File: src/main/java/com/iemr/flw/service/impl/IncentiveServiceImpl.java Method: saveIncentivesMaster
Lines: 49-51

Current Code (Buggy)

@Override
public String saveIncentivesMaster(List<IncentiveActivityDTO> activityDTOS) {
    try {
        activityDTOS.forEach(activityDTO -> {
            IncentiveActivity activity =
                    incentivesRepo.findIncentiveMasterByNameAndGroup(activityDTO.getName(), activityDTO.getGroup());

            if (activity == null) {
                activity = new IncentiveActivity();
                modelMapper.map(activityDTO, activity);
            } else {
                Long id = activity.getId();
                modelMapper.map(activityDTO, activity);
                activity.setId(id);
            }
            incentivesRepo.save(activity);
        });
        String saved = "";
        activityDTOS.forEach(dto -> saved.concat(dto.getGroup() + ": " + dto.getName()));  // BUG HERE
        return "saved master data for " + saved;  // Always returns "saved master data for "
    } catch (Exception e) {
        
    }
    return null;
}

The Problem

In Java, String objects are immutable. The concat() method does not modify the original string; instead, it returns a new string with the concatenated result. Since the return value is not assigned back to saved, the variable remains empty.

What Happens:

| Iteration | saved.concat(...) Result | saved Variable Value | |-----------|---------------------------|------------------------| | 1 | "Group1: Activity1" | "" (unchanged) |
| 2 | "Group2: Activity2" | "" (unchanged) |
| Final Return | - | "saved master data for " |

Impact

  • Incorrect API Response: The method always returns "saved master data for " without listing the actual saved records
  • Poor User Experience: API consumers cannot verify which incentive activities were saved
  • Debugging Difficulty: Developers cannot trace what data was processed from logs

Proposed Fix

Replace String with StringBuilder which is mutable and designed for such use cases:

@Override
public String saveIncentivesMaster(List<IncentiveActivityDTO> activityDTOS) {
    try {
        activityDTOS.forEach(activityDTO -> {
            IncentiveActivity activity =
                    incentivesRepo.findIncentiveMasterByNameAndGroup(activityDTO.getName(), activityDTO.getGroup());

            if (activity == null) {
                activity = new IncentiveActivity();
                modelMapper.map(activityDTO, activity);
            } else {
                Long id = activity.getId();
                modelMapper.map(activityDTO, activity);
                activity.setId(id);
            }
            incentivesRepo.save(activity);
        });
        
        // FIX: Use StringBuilder for mutable string operations
        StringBuilder saved = new StringBuilder();
        activityDTOS.forEach(dto -> 
            saved.append(dto.getGroup())
                 .append(": ")
                 .append(dto.getName())
                 .append(", ")
        );
        
        // Remove trailing ", " if present
        if (saved.length() > 0) {
            saved.setLength(saved.length() - 2);
        }
        
        return "saved master data for " + saved.toString();
    } catch (Exception e) {
        logger.error("Error saving incentive master data: " + e.getMessage(), e);
    }
    return null;
}

Alternative Fix (Java 8+ Streams)

String saved = activityDTOS.stream()
    .map(dto -> dto.getGroup() + ": " + dto.getName())
    .collect(Collectors.joining(", "));

return "saved master data for " + saved;

Additional Improvements Needed

  1. Empty Catch Block: The catch block at lines 52-54 is empty and should log the exception: java } catch (Exception e) { logger.error("Error in saveIncentivesMaster: " + e.getMessage(), e); }

  2. Return Value Consistency: Consider returning an empty string or a proper error message instead of null on failure.

Test Case to Verify

@Test
public void testSaveIncentivesMaster_ReturnsCorrectSummary() {
    // Arrange
    List<IncentiveActivityDTO> dtos = Arrays.asList(
        createDto("MATERNAL HEALTH", "ANC-1"),
        createDto("MATERNAL HEALTH", "ANC-FULL")
    );
    
    // Act
    String result = incentiveService.saveIncentivesMaster(dtos);
    
    // Assert
    assertEquals("saved master data for MATERNAL HEALTH: ANC-1, MATERNAL HEALTH: ANC-FULL", result);
}

Priority

Medium - This is a functional bug that affects API response accuracy but does not cause system crashes or data corruption.

Labels

bug, good first issue, help wanted, code-quality


References

📋 Description

JIRA ID:

Please provide a summary of the change and the motivation behind it. Include relevant context and details.


Summary by CodeRabbit

  • Bug Fixes
    • Corrected incentive master data saving so the UI now shows the full, accurate list of saved items instead of incomplete results.
    • Improved error handling when saving or retrieving incentive master data to make failures more reliable and clearer to the user.

# String Concatenation Logic Error in IncentiveServiceImpl

## Description

There is a critical bug in the `saveIncentivesMaster` method of `IncentiveServiceImpl.java` where `String.concat()` is incorrectly used within a `forEach` loop. Due to Java's String immutability, the concatenation result is discarded, causing the method to always return an empty string for the saved records summary.

## Location

**File:** `src/main/java/com/iemr/flw/service/impl/IncentiveServiceImpl.java`  
**Method:** `saveIncentivesMaster`  
**Lines:** 49-51

## Current Code (Buggy)

```java
@OverRide
public String saveIncentivesMaster(List<IncentiveActivityDTO> activityDTOS) {
    try {
        activityDTOS.forEach(activityDTO -> {
            IncentiveActivity activity =
                    incentivesRepo.findIncentiveMasterByNameAndGroup(activityDTO.getName(), activityDTO.getGroup());

            if (activity == null) {
                activity = new IncentiveActivity();
                modelMapper.map(activityDTO, activity);
            } else {
                Long id = activity.getId();
                modelMapper.map(activityDTO, activity);
                activity.setId(id);
            }
            incentivesRepo.save(activity);
        });
        String saved = "";
        activityDTOS.forEach(dto -> saved.concat(dto.getGroup() + ": " + dto.getName()));  // BUG HERE
        return "saved master data for " + saved;  // Always returns "saved master data for "
    } catch (Exception e) {
        
    }
    return null;
}
```

## The Problem

In Java, `String` objects are **immutable**. The `concat()` method does not modify the original string; instead, it returns a **new** string with the concatenated result. Since the return value is not assigned back to `saved`, the variable remains empty.

### What Happens:

| Iteration | `saved.concat(...)` Result | `saved` Variable Value |
|-----------|---------------------------|------------------------|
| 1 | `"Group1: Activity1"` | `""` (unchanged) |
| 2 | `"Group2: Activity2"` | `""` (unchanged) |
| Final Return | - | `"saved master data for "` |

## Impact

* **Incorrect API Response:** The method always returns `"saved master data for "` without listing the actual saved records
* **Poor User Experience:** API consumers cannot verify which incentive activities were saved
* **Debugging Difficulty:** Developers cannot trace what data was processed from logs

## Proposed Fix

Replace `String` with `StringBuilder` which is mutable and designed for such use cases:

```java
@OverRide
public String saveIncentivesMaster(List<IncentiveActivityDTO> activityDTOS) {
    try {
        activityDTOS.forEach(activityDTO -> {
            IncentiveActivity activity =
                    incentivesRepo.findIncentiveMasterByNameAndGroup(activityDTO.getName(), activityDTO.getGroup());

            if (activity == null) {
                activity = new IncentiveActivity();
                modelMapper.map(activityDTO, activity);
            } else {
                Long id = activity.getId();
                modelMapper.map(activityDTO, activity);
                activity.setId(id);
            }
            incentivesRepo.save(activity);
        });
        
        // FIX: Use StringBuilder for mutable string operations
        StringBuilder saved = new StringBuilder();
        activityDTOS.forEach(dto -> 
            saved.append(dto.getGroup())
                 .append(": ")
                 .append(dto.getName())
                 .append(", ")
        );
        
        // Remove trailing ", " if present
        if (saved.length() > 0) {
            saved.setLength(saved.length() - 2);
        }
        
        return "saved master data for " + saved.toString();
    } catch (Exception e) {
        logger.error("Error saving incentive master data: " + e.getMessage(), e);
    }
    return null;
}
```

## Alternative Fix (Java 8+ Streams)

```java
String saved = activityDTOS.stream()
    .map(dto -> dto.getGroup() + ": " + dto.getName())
    .collect(Collectors.joining(", "));

return "saved master data for " + saved;
```

## Additional Improvements Needed

1. **Empty Catch Block:** The catch block at lines 52-54 is empty and should log the exception:
   ```java
   } catch (Exception e) {
       logger.error("Error in saveIncentivesMaster: " + e.getMessage(), e);
   }
   ```

2. **Return Value Consistency:** Consider returning an empty string or a proper error message instead of `null` on failure.

## Test Case to Verify

```java
@test
public void testSaveIncentivesMaster_ReturnsCorrectSummary() {
    // Arrange
    List<IncentiveActivityDTO> dtos = Arrays.asList(
        createDto("MATERNAL HEALTH", "ANC-1"),
        createDto("MATERNAL HEALTH", "ANC-FULL")
    );
    
    // Act
    String result = incentiveService.saveIncentivesMaster(dtos);
    
    // Assert
    assertEquals("saved master data for MATERNAL HEALTH: ANC-1, MATERNAL HEALTH: ANC-FULL", result);
}
```

## Priority

**Medium** - This is a functional bug that affects API response accuracy but does not cause system crashes or data corruption.

## Labels

`bug`, `good first issue`, `help wanted`, `code-quality`

---

## References

* [Java String Immutability](https://docs.oracle.com/javase/tutorial/java/data/strings.html)
* [StringBuilder vs String Concatenation Performance](https://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html)
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 3, 2026

📝 Walkthrough

Walkthrough

IncentiveServiceImpl adds an SLF4J Logger, emits error logs with stack traces in catch blocks, and replaces an ineffective String.concat() pattern in saveIncentivesMaster(...) with a StringBuilder plus null-safe Objects.toString(...) to return a correct comma-separated summary of saved group/name pairs.

Changes

Cohort / File(s) Summary
Logging and String Building Fix
src/main/java/com/iemr/flw/service/impl/IncentiveServiceImpl.java
Added SLF4J Logger and error logging in catch blocks. Refactored saveIncentivesMaster(...) to use StringBuilder and Objects.toString(...) to produce a correctly formatted saved master data for ... message, replacing ineffective String.concat() usage.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 I found a bug and gave it a nudge,
I added logs so errors don't smudge,
StringBuilder hops in, neat and spry,
Group:name pairs lined up by and by,
A little rabbit's clap—code fixed with a sigh.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'Add logging to IncentiveServiceImpl methods' accurately reflects the main change: adding SLF4J logger and error logging to catch blocks in the service implementation.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
src/main/java/com/iemr/flw/service/impl/IncentiveServiceImpl.java (4)

25-25: Consider making the logger static.

Logger instances are typically declared as private static final by convention. This avoids creating a new logger reference per instance and is the idiomatic SLF4J pattern.

♻️ Suggested change
-    private final Logger logger = LoggerFactory.getLogger(IncentiveServiceImpl.class);
+    private static final Logger logger = LoggerFactory.getLogger(IncentiveServiceImpl.class);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/com/iemr/flw/service/impl/IncentiveServiceImpl.java` at line
25, Change the instance logger field in IncentiveServiceImpl to a class-level
logger by making the existing private final Logger logger a private static final
Logger so it follows the SLF4J idiom; update the declaration for the field named
logger in class IncentiveServiceImpl accordingly to avoid per-instance logger
creation.

75-78: Use parameterized logging for consistency.

Same suggestion as above—use SLF4J's parameterized format.

♻️ Suggested change
-            logger.error("Error getting incentive master data: " + e.getMessage(), e);
+            logger.error("Error getting incentive master data: {}", e.getMessage(), e);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/com/iemr/flw/service/impl/IncentiveServiceImpl.java` around
lines 75 - 78, In the catch block inside IncentiveServiceImpl where you
currently call logger.error("Error getting incentive master data: " +
e.getMessage(), e), change it to use SLF4J parameterized logging: use a message
with a {} placeholder and pass e.getMessage() as the parameter and the throwable
as the last argument; update the logger call in that catch block (the one
handling Exception e) accordingly to maintain the same message and include the
exception object.

81-88: Consider adding error handling to getAllIncentivesByUserId for consistency.

This method lacks try-catch and logging, unlike saveIncentivesMaster and getIncentiveMaster. If an exception occurs (e.g., from recordRepo.findRecordsByAsha), it will propagate unlogged.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/com/iemr/flw/service/impl/IncentiveServiceImpl.java` around
lines 81 - 88, Wrap the body of getAllIncentivesByUserId in a try-catch that
mirrors the other methods: try to call recordRepo.findRecordsByAsha, map entries
via modelMapper.map into dtos and return gson.toJson(dtos); catch Exception e,
log the error with the same logger used elsewhere (e.g., logger.error("Error in
getAllIncentivesByUserId for ashaId={}, from={}, to={}", request.getAshaId(),
request.getFromDate(), request.getToDate(), e)) and return an empty JSON array
(e.g., gson.toJson(new ArrayList<>())) to avoid leaking exceptions to callers
while preserving the method signature.

59-62: Use parameterized logging instead of string concatenation.

SLF4J supports parameterized messages which avoid string concatenation overhead when the log level is disabled.

♻️ Suggested change
-            logger.error("Error saving incentive master data: " + e.getMessage(), e);
+            logger.error("Error saving incentive master data: {}", e.getMessage(), e);

Note: As mentioned in the PR description, returning null on failure could be improved by returning a consistent error value or throwing a custom exception. This would provide better feedback to the controller.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/com/iemr/flw/service/impl/IncentiveServiceImpl.java` around
lines 59 - 62, In the catch block inside IncentiveServiceImpl (the
save/incentive master data save routine) replace the string-concatenated logger
call logger.error("Error saving incentive master data: " + e.getMessage(), e)
with a parameterized SLF4J call (e.g. logger.error("Error saving incentive
master data: {}", e.getMessage(), e) or simply logger.error("Error saving
incentive master data", e)) to avoid unnecessary string concatenation;
additionally consider replacing the method's current return-null-on-exception
behavior with a consistent error response (throw a custom exception or return an
Optional/Result) to surface failures to the controller.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/com/iemr/flw/service/impl/IncentiveServiceImpl.java`:
- Around line 52-58: The current StringBuilder loop in IncentiveServiceImpl that
builds the saved string from activityDTOS can produce literal "null" when
dto.getGroup() or dto.getName() is null; update the construction in the
activityDTOS.forEach block (or its containing method) to defensively convert
nulls to empty strings (e.g., use Objects.toString(dto.getGroup(), "") and
Objects.toString(dto.getName(), "")) or skip null fields before appending, then
trim the trailing separator as before so the returned "saved master data for
..." never contains "null".

---

Nitpick comments:
In `@src/main/java/com/iemr/flw/service/impl/IncentiveServiceImpl.java`:
- Line 25: Change the instance logger field in IncentiveServiceImpl to a
class-level logger by making the existing private final Logger logger a private
static final Logger so it follows the SLF4J idiom; update the declaration for
the field named logger in class IncentiveServiceImpl accordingly to avoid
per-instance logger creation.
- Around line 75-78: In the catch block inside IncentiveServiceImpl where you
currently call logger.error("Error getting incentive master data: " +
e.getMessage(), e), change it to use SLF4J parameterized logging: use a message
with a {} placeholder and pass e.getMessage() as the parameter and the throwable
as the last argument; update the logger call in that catch block (the one
handling Exception e) accordingly to maintain the same message and include the
exception object.
- Around line 81-88: Wrap the body of getAllIncentivesByUserId in a try-catch
that mirrors the other methods: try to call recordRepo.findRecordsByAsha, map
entries via modelMapper.map into dtos and return gson.toJson(dtos); catch
Exception e, log the error with the same logger used elsewhere (e.g.,
logger.error("Error in getAllIncentivesByUserId for ashaId={}, from={}, to={}",
request.getAshaId(), request.getFromDate(), request.getToDate(), e)) and return
an empty JSON array (e.g., gson.toJson(new ArrayList<>())) to avoid leaking
exceptions to callers while preserving the method signature.
- Around line 59-62: In the catch block inside IncentiveServiceImpl (the
save/incentive master data save routine) replace the string-concatenated logger
call logger.error("Error saving incentive master data: " + e.getMessage(), e)
with a parameterized SLF4J call (e.g. logger.error("Error saving incentive
master data: {}", e.getMessage(), e) or simply logger.error("Error saving
incentive master data", e)) to avoid unnecessary string concatenation;
additionally consider replacing the method's current return-null-on-exception
behavior with a consistent error response (throw a custom exception or return an
Optional/Result) to surface failures to the controller.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6920bf3e-748e-4bb1-8990-46aa2bfc9f7e

📥 Commits

Reviewing files that changed from the base of the PR and between 690dbee and 7324ec9.

📒 Files selected for processing (1)
  • src/main/java/com/iemr/flw/service/impl/IncentiveServiceImpl.java

Updated string concatenation to handle null values using Objects.toString.
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
src/main/java/com/iemr/flw/service/impl/IncentiveServiceImpl.java (2)

61-61: Use SLF4J parameterized logging instead of string concatenation.

Lines 61 and 77 concatenate e.getMessage() into the log message; use placeholders ({}) to avoid unnecessary string building.

♻️ Suggested refactor
-            logger.error("Error saving incentive master data: " + e.getMessage(), e);
+            logger.error("Error saving incentive master data: {}", e.getMessage(), e);
-            logger.error("Error getting incentive master data: " + e.getMessage(), e);
+            logger.error("Error getting incentive master data: {}", e.getMessage(), e);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/com/iemr/flw/service/impl/IncentiveServiceImpl.java` at line
61, The logger.error calls in IncentiveServiceImpl that currently use string
concatenation with e.getMessage() should be changed to SLF4J parameterized
logging: replace the concatenated messages in the two spots (the logger.error
call that reads "Error saving incentive master data: " + e.getMessage() and the
similar call later) with a template using {} and pass e.getMessage() as a
parameter, and pass the exception e as the final argument so the signature is
logger.error("Error saving incentive master data: {}", e.getMessage(), e) (apply
the same pattern to the second occurrence).

26-26: Use a static logger constant for this class.

Line 26 currently creates an instance logger; prefer a private static final logger constant for standard SLF4J usage.

♻️ Suggested refactor
-    private final Logger logger = LoggerFactory.getLogger(IncentiveServiceImpl.class);
+    private static final Logger LOGGER = LoggerFactory.getLogger(IncentiveServiceImpl.class);

Note: Update the logger usages on lines 61 and 77 from logger.error(...) to LOGGER.error(...) after making this change.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/com/iemr/flw/service/impl/IncentiveServiceImpl.java` at line
26, Replace the instance logger field in class IncentiveServiceImpl with a
static constant by changing the current private final Logger logger to a private
static final Logger LOGGER =
LoggerFactory.getLogger(IncentiveServiceImpl.class); then update all usages
(notably the calls currently using logger.error on the methods referenced) to
use LOGGER.error(...) instead of logger.error(...); ensure imports remain same
and the constant name is used consistently across the class.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/main/java/com/iemr/flw/service/impl/IncentiveServiceImpl.java`:
- Line 61: The logger.error calls in IncentiveServiceImpl that currently use
string concatenation with e.getMessage() should be changed to SLF4J
parameterized logging: replace the concatenated messages in the two spots (the
logger.error call that reads "Error saving incentive master data: " +
e.getMessage() and the similar call later) with a template using {} and pass
e.getMessage() as a parameter, and pass the exception e as the final argument so
the signature is logger.error("Error saving incentive master data: {}",
e.getMessage(), e) (apply the same pattern to the second occurrence).
- Line 26: Replace the instance logger field in class IncentiveServiceImpl with
a static constant by changing the current private final Logger logger to a
private static final Logger LOGGER =
LoggerFactory.getLogger(IncentiveServiceImpl.class); then update all usages
(notably the calls currently using logger.error on the methods referenced) to
use LOGGER.error(...) instead of logger.error(...); ensure imports remain same
and the constant name is used consistently across the class.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d3f78140-9dae-4a1f-9325-702d90beddfd

📥 Commits

Reviewing files that changed from the base of the PR and between 7324ec9 and 63f4241.

📒 Files selected for processing (1)
  • src/main/java/com/iemr/flw/service/impl/IncentiveServiceImpl.java

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant