Add basic logging

Add basic logging of exceptions, warnings, info and debug statements.
The implementation used is Logback.
This commit is contained in:
2019-03-11 21:25:22 +01:00
parent a0884e1f21
commit a27d26596b
10 changed files with 170 additions and 46 deletions

View File

@@ -1,6 +1,8 @@
package de.financer.config;
import de.jollyday.HolidayCalendar;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@@ -12,6 +14,8 @@ import java.util.Optional;
@Configuration
@ConfigurationProperties(prefix = "financer")
public class FinancerConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(FinancerConfig.class);
private String countryCode;
private String state;
private String dateFormat;
@@ -33,8 +37,8 @@ public class FinancerConfig {
}
/**
* @return the {@link HolidayCalendar} used to calculate the holidays. Internally uses the country code
* specified via {@link FinancerConfig#getCountryCode}.
* @return the {@link HolidayCalendar} used to calculate the holidays. Internally uses the country code specified
* via {@link FinancerConfig#getCountryCode}.
*/
public HolidayCalendar getHolidayCalendar() {
final Optional<HolidayCalendar> optionalHoliday = Arrays.asList(HolidayCalendar.values()).stream()
@@ -42,7 +46,10 @@ public class FinancerConfig {
.findFirst();
if (!optionalHoliday.isPresent()) {
// TODO log info about default DE
LOGGER.warn(String
.format("Use Germany as fallback country for holiday calculations. Configured country code is: %s. " +
"This does not match any valid country code as specified by Jollyday",
this.countryCode));
}
return optionalHoliday.orElse(HolidayCalendar.GERMANY);
@@ -57,12 +64,11 @@ public class FinancerConfig {
}
/**
* @return the date format used in e.g. the
* {@link de.financer.service.TransactionService#createTransaction(String, String, Long, String, String)
* TransactionService#createTransaction} or
* {@link de.financer.service.RecurringTransactionService#createRecurringTransaction(String, String, Long, String,
* String, String, String, String) RecurringTransactionService#createRecurringTransaction} methods. Used to parse
* the client-supplied date string to proper {@link java.time.LocalDate LocalDate} objects
* @return the date format used in e.g. the {@link de.financer.service.TransactionService#createTransaction(String,
* String, Long, String, String) TransactionService#createTransaction} or {@link
* de.financer.service.RecurringTransactionService#createRecurringTransaction(String, String, Long, String, String,
* String, String, String) RecurringTransactionService#createRecurringTransaction} methods. Used to parse the
* client-supplied date string to proper {@link java.time.LocalDate LocalDate} objects
*/
public String getDateFormat() {
return dateFormat;
@@ -71,9 +77,10 @@ public class FinancerConfig {
public void setDateFormat(String dateFormat) {
try {
DateTimeFormatter.ofPattern(dateFormat);
}
catch (IllegalArgumentException e) {
// TODO log info about default dd.MM.yyyy
} catch (IllegalArgumentException e) {
LOGGER.warn(String
.format("Use 'dd.MM.yyyy' as fallback for the date format because the configured format '%s' " +
"cannot be parsed!", dateFormat), e);
dateFormat = "dd.MM.yyyy";
}

View File

@@ -1,7 +1,10 @@
package de.financer.controller;
import de.financer.ResponseReason;
import de.financer.model.Account;
import de.financer.service.AccountService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -11,6 +14,8 @@ import org.springframework.web.bind.annotation.RestController;
@RequestMapping("accounts")
public class AccountController {
private static final Logger LOGGER = LoggerFactory.getLogger(AccountController.class);
@Autowired
private AccountService accountService;
@@ -36,6 +41,16 @@ public class AccountController {
@RequestMapping("createAccount")
public ResponseEntity createAccount(String key, String type) {
return this.accountService.createAccount(key, type).toResponseEntity();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("/accounts/createAccount got parameters: %s, %s", key, type));
}
final ResponseReason responseReason = this.accountService.createAccount(key, type);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("/accounts/createAccount returns with %s", responseReason.name()));
}
return responseReason.toResponseEntity();
}
}

View File

@@ -1,7 +1,10 @@
package de.financer.controller;
import de.financer.ResponseReason;
import de.financer.model.RecurringTransaction;
import de.financer.service.RecurringTransactionService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -13,6 +16,8 @@ import java.util.Optional;
@RequestMapping("recurringTransactions")
public class RecurringTransactionController {
private static final Logger LOGGER = LoggerFactory.getLogger(RecurringTransactionController.class);
@Autowired
private RecurringTransactionService recurringTransactionService;
@@ -35,17 +40,44 @@ public class RecurringTransactionController {
public ResponseEntity createRecurringTransaction(String fromAccountKey, String toAccountKey, Long amount,
String description, String holidayWeekendType,
String intervalType, String firstOccurrence,
String lastOccurrence) {
return this.recurringTransactionService.createRecurringTransaction(fromAccountKey, toAccountKey, amount,
description, holidayWeekendType,
intervalType, firstOccurrence,
lastOccurrence)
.toResponseEntity();
String lastOccurrence
) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String
.format("/recurringTransactions/createRecurringTransaction got parameters: %s, %s, %s, %s, %s, " +
"%s, %s, %s", fromAccountKey, toAccountKey, amount, description, holidayWeekendType,
intervalType, firstOccurrence, lastOccurrence));
}
final ResponseReason responseReason = this.recurringTransactionService
.createRecurringTransaction(fromAccountKey, toAccountKey, amount, description, holidayWeekendType,
intervalType, firstOccurrence, lastOccurrence);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String
.format("/recurringTransactions/createRecurringTransaction returns with %s", responseReason
.name()));
}
return responseReason.toResponseEntity();
}
@RequestMapping("createTransaction")
public ResponseEntity createTransaction(String recurringTransactionId, Long amount) {
return this.recurringTransactionService.createTransaction(recurringTransactionId, Optional.ofNullable(amount))
.toResponseEntity();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String
.format("/recurringTransactions/createTransaction got parameters: %s, %s",
recurringTransactionId, amount));
}
final ResponseReason responseReason = this.recurringTransactionService
.createTransaction(recurringTransactionId, Optional.ofNullable(amount));
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String
.format("/recurringTransactions/createTransaction returns with %s", responseReason.name()));
}
return responseReason.toResponseEntity();
}
}

View File

@@ -1,7 +1,10 @@
package de.financer.controller;
import de.financer.ResponseReason;
import de.financer.model.Transaction;
import de.financer.service.TransactionService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -10,6 +13,8 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("transactions")
public class TransactionController {
private static final Logger LOGGER = LoggerFactory.getLogger(TransactionController.class);
@Autowired
private TransactionService transactionService;
@@ -25,8 +30,21 @@ public class TransactionController {
@RequestMapping(value = "createTransaction")
public ResponseEntity createTransaction(String fromAccountKey, String toAccountKey, Long amount, String date,
String description) {
return this.transactionService.createTransaction(fromAccountKey, toAccountKey, amount, date, description)
.toResponseEntity();
String description
) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String
.format("/transactions/createTransaction got parameters: %s, %s, %s, %s, %s",
fromAccountKey, toAccountKey, amount, date, description));
}
final ResponseReason responseReason = this.transactionService
.createTransaction(fromAccountKey, toAccountKey, amount, date, description);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("/transactions/createTransaction returns with %s", responseReason.name()));
}
return responseReason.toResponseEntity();
}
}

View File

@@ -6,6 +6,8 @@ import de.financer.model.Account;
import de.financer.model.AccountStatus;
import de.financer.model.AccountType;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
@@ -16,6 +18,8 @@ import java.util.stream.Collectors;
@Service
public class AccountService {
private static final Logger LOGGER = LoggerFactory.getLogger(AccountService.class);
@Autowired
private AccountRepository accountRepository;
@@ -95,7 +99,8 @@ public class AccountService {
this.accountRepository.save(account);
}
catch (Exception e) {
// TODO log and check for unique constraint exception so we can return a more specific error
LOGGER.error(String.format("Could not save account %s|%s", key, type), e);
return ResponseReason.UNKNOWN_ERROR;
}

View File

@@ -10,6 +10,8 @@ import de.financer.model.RecurringTransaction;
import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
@@ -24,6 +26,7 @@ import java.util.stream.Collectors;
@Service
public class RecurringTransactionService {
private static final Logger LOGGER = LoggerFactory.getLogger(RecurringTransactionService.class);
@Autowired
private RecurringTransactionRepository recurringTransactionRepository;
@@ -48,6 +51,8 @@ public class RecurringTransactionService {
final Account account = this.accountService.getAccountByKey(accountKey);
if (account == null) {
LOGGER.warn(String.format("Account with key %s not found!", accountKey));
return Collections.emptyList();
}
@@ -250,8 +255,7 @@ public class RecurringTransactionService {
response = ResponseReason.OK;
} catch (Exception e) {
// TODO log
e.printStackTrace();
LOGGER.error("Could not create recurring transaction!", e);
response = ResponseReason.UNKNOWN_ERROR;
}

View File

@@ -6,6 +6,8 @@ import de.financer.model.AccountType;
import de.financer.model.IntervalType;
import de.jollyday.HolidayManager;
import de.jollyday.ManagerParameters;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -18,13 +20,12 @@ import java.util.*;
import static de.financer.model.AccountType.*;
/**
* This service encapsulates methods that form basic logic rules.
* While most of the logic could be placed elsewhere this service provides
* centralized access to these rules. Placing them in here also enables easy
* unit testing.
* This service encapsulates methods that form basic logic rules. While most of the logic could be placed elsewhere this
* service provides centralized access to these rules. Placing them in here also enables easy unit testing.
*/
@Service
public class RuleService implements InitializingBean {
private static final Logger LOGGER = LoggerFactory.getLogger(RuleService.class);
@Autowired
private FinancerConfig financerConfig;
@@ -65,10 +66,11 @@ public class RuleService implements InitializingBean {
/**
* This method returns the multiplier for the given from account.
* <p>
* The multiplier controls whether the current amount of the given from account is increased or
* decreased depending on the {@link AccountType} of the given account.
* The multiplier controls whether the current amount of the given from account is increased or decreased depending
* on the {@link AccountType} of the given account.
*
* @param fromAccount the from account to get the multiplier for
*
* @return the multiplier, either <code>1</code> or <code>-1</code>
*/
public long getMultiplierFromAccount(Account fromAccount) {
@@ -89,16 +91,20 @@ public class RuleService implements InitializingBean {
return 1L;
}
LOGGER.warn(String
.format("Unknown or invalid account type in getMultiplierFromAccount: %s", accountType.name()));
return 1L;
}
/**
* This method returns the multiplier for the given to account.
* <p>
* The multiplier controls whether the current amount of the given to account is increased or
* decreased depending on the {@link AccountType} of the given account.
* The multiplier controls whether the current amount of the given to account is increased or decreased depending on
* the {@link AccountType} of the given account.
*
* @param toAccount the to account to get the multiplier for
*
* @return the multiplier, either <code>1</code> or <code>-1</code>
*/
public long getMultiplierToAccount(Account toAccount) {
@@ -117,27 +123,33 @@ public class RuleService implements InitializingBean {
return 1L;
}
LOGGER.warn(String
.format("Unknown or invalid account type in getMultiplierToAccount: %s", accountType.name()));
return -1L;
}
/**
* This method validates whether the booking from <code>fromAccount</code> to <code>toAccount</code>
* is valid, e.g. booking directly from an {@link AccountType#INCOME INCOME} to an {@link AccountType#EXPENSE EXPENSE}
* account does not make sense and is declared as invalid.
* This method validates whether the booking from <code>fromAccount</code> to <code>toAccount</code> is valid, e.g.
* booking directly from an {@link AccountType#INCOME INCOME} to an {@link AccountType#EXPENSE EXPENSE} account does
* not make sense and is declared as invalid.
*
* @param fromAccount the account to subtract the money from
* @param toAccount the account to add the money to
* @return <code>true</code> if the from-&gt;to relationship of the given accounts is valid, <code>false</code> otherwise
* @param toAccount the account to add the money to
*
* @return <code>true</code> if the from-&gt;to relationship of the given accounts is valid, <code>false</code>
* otherwise
*/
public boolean isValidBooking(Account fromAccount, Account toAccount) {
return this.bookingRules.get(fromAccount.getType()).contains(toAccount.getType());
}
/**
* This method gets the {@link Period} for the given {@link IntervalType},
* e.g. a period of three months for {@link IntervalType#QUARTERLY}.
* This method gets the {@link Period} for the given {@link IntervalType}, e.g. a period of three months for {@link
* IntervalType#QUARTERLY}.
*
* @param intervalType to get the period for
*
* @return the period matching the interval type
*/
public Period getPeriodForInterval(IntervalType intervalType) {
@@ -148,18 +160,24 @@ public class RuleService implements InitializingBean {
* This method checks whether the given date is a holiday in the configured country and state.
*
* @param now the date to check
*
* @return <code>true</code> if the given date is a holiday, <code>false</code> otherwise
*/
public boolean isHoliday(LocalDate now) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("Use state '%s' for holiday calculation", this.financerConfig.getState()));
}
return HolidayManager.getInstance(ManagerParameters.create(this.financerConfig.getHolidayCalendar()))
.isHoliday(now, this.financerConfig.getState());
}
/**
* This method checks whether the given date is a weekend day, i.e. whether it's a
* {@link DayOfWeek#SATURDAY} or {@link DayOfWeek#SUNDAY}.
* This method checks whether the given date is a weekend day, i.e. whether it's a {@link DayOfWeek#SATURDAY} or
* {@link DayOfWeek#SUNDAY}.
*
* @param now the date to check
*
* @return <code>true</code> if the given date is a weekend day, <code>false</code> otherwise
*/
public boolean isWeekend(LocalDate now) {

View File

@@ -6,6 +6,8 @@ import de.financer.dba.TransactionRepository;
import de.financer.model.Account;
import de.financer.model.RecurringTransaction;
import de.financer.model.Transaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
@@ -18,6 +20,8 @@ import java.util.Collections;
@Service
public class TransactionService {
private static final Logger LOGGER = LoggerFactory.getLogger(TransactionService.class);
@Autowired
private AccountService accountService;
@@ -47,6 +51,8 @@ public class TransactionService {
final Account account = this.accountService.getAccountByKey(accountKey);
if (account == null) {
LOGGER.warn(String.format("Account with key %s not found!", accountKey));
return Collections.emptyList();
}
@@ -89,7 +95,7 @@ public class TransactionService {
response = ResponseReason.OK;
} catch (Exception e) {
// TODO log
LOGGER.error("Could not create transaction!", e);
response = ResponseReason.UNKNOWN_ERROR;
}

View File

@@ -4,6 +4,8 @@ import de.financer.config.FinancerConfig;
import de.financer.model.RecurringTransaction;
import de.financer.service.RecurringTransactionService;
import org.apache.commons.collections4.IterableUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.MailException;
import org.springframework.mail.SimpleMailMessage;
@@ -14,6 +16,8 @@ import org.springframework.stereotype.Component;
@Component
public class SendRecurringTransactionReminderTask {
private static final Logger LOGGER = LoggerFactory.getLogger(SendRecurringTransactionReminderTask.class);
@Autowired
private RecurringTransactionService recurringTransactionService;
@@ -25,13 +29,23 @@ public class SendRecurringTransactionReminderTask {
@Scheduled(cron = "0 30 0 * * *")
public void sendReminder() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Enter recurring transaction reminder task");
}
final Iterable<RecurringTransaction> recurringTransactions = this.recurringTransactionService.getAllDueToday();
// If no recurring transaction is due today we don't need to send a reminder
if (IterableUtils.isEmpty(recurringTransactions)) {
LOGGER.info("No recurring transactions due today!");
return; // early return
}
LOGGER.info(String
.format("%s recurring transaction are due today and are about to be included in the reminder email",
IterableUtils.size(recurringTransactions)));
final StringBuilder reminderBuilder = new StringBuilder();
reminderBuilder.append("The following recurring transactions are due today:")
@@ -63,7 +77,10 @@ public class SendRecurringTransactionReminderTask {
try {
this.mailSender.send(msg);
} catch (MailException e) {
// TODO log
LOGGER.error("Could not send recurring transaction email reminder!", e);
LOGGER.info("Dumb email reminder content because the sending failed");
LOGGER.info(reminderBuilder.toString());
}
}
}

View File

@@ -14,6 +14,8 @@ info.build.group=@project.groupId@
info.build.artifact=@project.artifactId@
info.build.version=@project.version@
logging.level.de.financer=DEBUG
# Country code for holiday checks
# Mostly an uppercase ISO 3166 2-letter code
# For a complete list of the supported codes see https://github.com/svendiedrichsen/jollyday/blob/master/src/main/java/de/jollyday/HolidayCalendar.java