killbill-memoizeit

Merge branch 'enhanced-logging' into work-for-release-0.16.4 Signed-off-by:

4/19/2016 2:46:46 PM

Changes

util/src/main/java/org/killbill/billing/util/callcontext/MigrationCallContext.java 43(+0 -43)

Details

diff --git a/account/src/main/java/org/killbill/billing/account/dao/DefaultAccountDao.java b/account/src/main/java/org/killbill/billing/account/dao/DefaultAccountDao.java
index 19eb29a..7d9815d 100644
--- a/account/src/main/java/org/killbill/billing/account/dao/DefaultAccountDao.java
+++ b/account/src/main/java/org/killbill/billing/account/dao/DefaultAccountDao.java
@@ -94,7 +94,7 @@ public class DefaultAccountDao extends EntityDaoBase<AccountModelDao, Account, A
         try {
             eventBus.postFromTransaction(creationEvent, entitySqlDaoWrapperFactory.getHandle().getConnection());
         } catch (final EventBusException e) {
-            log.warn("Failed to post account creation event for account " + savedAccount.getId(), e);
+            log.warn("Failed to post account creation event for accountId='{}'", savedAccount.getId(), e);
         }
     }
 
@@ -166,7 +166,7 @@ public class DefaultAccountDao extends EntityDaoBase<AccountModelDao, Account, A
                 try {
                     eventBus.postFromTransaction(changeEvent, entitySqlDaoWrapperFactory.getHandle().getConnection());
                 } catch (final EventBusException e) {
-                    log.warn("Failed to post account change event for account " + accountId, e);
+                    log.warn("Failed to post account change event for accountId='{}'", accountId, e);
                 }
 
                 return null;
@@ -205,7 +205,7 @@ public class DefaultAccountDao extends EntityDaoBase<AccountModelDao, Account, A
                 try {
                     eventBus.postFromTransaction(changeEvent, entitySqlDaoWrapperFactory.getHandle().getConnection());
                 } catch (final EventBusException e) {
-                    log.warn("Failed to post account change event for account " + accountId, e);
+                    log.warn("Failed to post account change event for accountId='{}'", accountId, e);
                 }
                 return null;
             }
diff --git a/beatrix/src/main/java/org/killbill/billing/beatrix/extbus/BeatrixListener.java b/beatrix/src/main/java/org/killbill/billing/beatrix/extbus/BeatrixListener.java
index 31389da..98a8df8 100644
--- a/beatrix/src/main/java/org/killbill/billing/beatrix/extbus/BeatrixListener.java
+++ b/beatrix/src/main/java/org/killbill/billing/beatrix/extbus/BeatrixListener.java
@@ -106,10 +106,10 @@ public class BeatrixListener {
             // However when using InMemoryBus, this can happen as there is no retry logic (at the 'ext' bus level) and so we should re-throw at this level to kick-in the retry logic from the 'main' bus
             // (The use of RuntimeException is somewhat arbitrary)
             //
-            log.warn("Failed to dispatch external bus events", e);
+            log.warn("Failed to post event {}", event, e);
             throw new RuntimeException(e);
         } catch (JsonProcessingException e) {
-            log.warn("Failed to dispatch external bus events", e);
+            log.warn("Failed to post event {}", event, e);
         }
     }
 
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/caching/CatalogCacheInvalidationCallback.java b/catalog/src/main/java/org/killbill/billing/catalog/caching/CatalogCacheInvalidationCallback.java
index 63548dc..852cd06 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/caching/CatalogCacheInvalidationCallback.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/caching/CatalogCacheInvalidationCallback.java
@@ -38,7 +38,7 @@ public class CatalogCacheInvalidationCallback implements CacheInvalidationCallba
 
     @Override
     public void invalidateCache(TenantKey key, final Object cookie, final InternalTenantContext tenantContext) {
-        log.info("Invalidate catalog cache for tenant {} ", tenantContext.getTenantRecordId());
+        log.info("Invalidate catalog cache for tenantRecordId='{}'", tenantContext.getTenantRecordId());
         catalogCache.clearCatalog(tenantContext);
     }
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheCatalogCache.java b/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheCatalogCache.java
index 44e704d..5713049 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheCatalogCache.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheCatalogCache.java
@@ -155,7 +155,7 @@ public class EhCacheCatalogCache implements CatalogCache {
             this.defaultCatalog = loader.loadDefaultCatalog("EmptyCatalog.xml");
         } catch (final CatalogApiException e) {
             this.defaultCatalog = new VersionedCatalog();
-            logger.warn("Exception loading EmptyCatalog - should never happen!", e);
+            logger.error("Exception loading EmptyCatalog - should never happen!", e);
         }
     }
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultCatalogService.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultCatalogService.java
index 053122c..cf17853 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultCatalogService.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultCatalogService.java
@@ -72,7 +72,7 @@ public class DefaultCatalogService implements KillbillService, CatalogService {
                 // In multi-tenant mode, the property is not required
                 if (config.getCatalogURI() != null && !config.getCatalogURI().isEmpty()) {
                     catalogCache.loadDefaultCatalog(config.getCatalogURI());
-                    log.info("Successfully loaded the default catalog " + config.getCatalogURI());
+                    log.info("Successfully loaded the default catalog {}", config.getCatalogURI());
                 }
                 isInitialized = true;
             } catch (Exception e) {
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/provider/DefaultCatalogProviderPluginRegistry.java b/catalog/src/main/java/org/killbill/billing/catalog/provider/DefaultCatalogProviderPluginRegistry.java
index e7e4005..d29ce36 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/provider/DefaultCatalogProviderPluginRegistry.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/provider/DefaultCatalogProviderPluginRegistry.java
@@ -41,13 +41,13 @@ public class DefaultCatalogProviderPluginRegistry implements OSGIServiceRegistra
 
     @Override
     public void registerService(final OSGIServiceDescriptor desc, final CatalogPluginApi service) {
-        log.info("DefaultInvoiceProviderPluginRegistry registering service " + desc.getRegistrationName());
+        log.info("Registering service='{}'", desc.getRegistrationName());
         pluginsByName.put(desc.getRegistrationName(), service);
     }
 
     @Override
     public void unregisterService(final String serviceName) {
-        log.info("DefaultInvoiceProviderPluginRegistry unregistering service " + serviceName);
+        log.info("Unregistering service='{}'", serviceName);
         pluginsByName.remove(serviceName);
     }
 
diff --git a/currency/src/main/java/org/killbill/billing/currency/DefaultCurrencyProviderPluginRegistry.java b/currency/src/main/java/org/killbill/billing/currency/DefaultCurrencyProviderPluginRegistry.java
index abb6abd..310eb05 100644
--- a/currency/src/main/java/org/killbill/billing/currency/DefaultCurrencyProviderPluginRegistry.java
+++ b/currency/src/main/java/org/killbill/billing/currency/DefaultCurrencyProviderPluginRegistry.java
@@ -42,13 +42,13 @@ public class DefaultCurrencyProviderPluginRegistry implements OSGIServiceRegistr
 
     @Override
     public void registerService(final OSGIServiceDescriptor desc, final CurrencyPluginApi service) {
-        log.info("DefaultCurrencyProviderPluginRegistry registering service " + desc.getRegistrationName());
+        log.info("Registering service='{}'", desc.getRegistrationName());
         pluginsByName.put(desc.getRegistrationName(), service);
     }
 
     @Override
     public void unregisterService(final String serviceName) {
-        log.info("DefaultCurrencyProviderPluginRegistry unregistering service " + serviceName);
+        log.info("Unregistering service='{}'", serviceName);
         pluginsByName.remove(serviceName);
     }
 
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java
index e74f7ef..959435f 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java
@@ -204,7 +204,7 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
                                                       try {
                                                           return getSubscriptionBundle(subscriptionBaseBundle.getId(), context);
                                                       } catch (final SubscriptionApiException e) {
-                                                          log.warn("Error retrieving subscription", e);
+                                                          log.warn("Error retrieving bundleId='{}'", subscriptionBaseBundle.getId(), e);
                                                           return null;
                                                       }
                                                   }
@@ -228,7 +228,7 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
                                                       try {
                                                           return getSubscriptionBundle(subscriptionBaseBundle.getId(), context);
                                                       } catch (final SubscriptionApiException e) {
-                                                          log.warn("Error retrieving subscription", e);
+                                                          log.warn("Error retrieving bundleId='{}'", subscriptionBaseBundle.getId(), e);
                                                           return null;
                                                       }
                                                   }
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java
index 1198f44..7d1ef3d 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java
@@ -189,7 +189,7 @@ public class DefaultEntitlementApiBase {
                     try {
                         eventBus.post(event);
                     } catch (EventBusException e) {
-                        log.warn("Failed to post bus event for pause operation on bundle " + bundleId);
+                        log.warn("Failed to post event {}", event, e);
                     }
 
                 } catch (SubscriptionBaseApiException e) {
@@ -241,7 +241,7 @@ public class DefaultEntitlementApiBase {
                     try {
                         eventBus.post(event);
                     } catch (EventBusException e) {
-                        log.warn("Failed to post bus event for resume operation on bundle " + bundleId);
+                        log.warn("Failed to post event {}", event, e);
                     }
 
                 } catch (SubscriptionBaseApiException e) {
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/dao/DefaultBlockingStateDao.java b/entitlement/src/main/java/org/killbill/billing/entitlement/dao/DefaultBlockingStateDao.java
index cb1551e..7615862 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/dao/DefaultBlockingStateDao.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/dao/DefaultBlockingStateDao.java
@@ -334,7 +334,7 @@ public class DefaultBlockingStateDao extends EntityDaoBase<BlockingStateModelDao
         try {
             eventBus.postFromTransaction(event, entitySqlDaoWrapperFactory.getHandle().getConnection());
         } catch (final EventBusException e) {
-            log.warn("Failed to post event {}", e);
+            log.warn("Failed to post event {}", event, e);
         }
     }
 
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/DefaultEntitlementService.java b/entitlement/src/main/java/org/killbill/billing/entitlement/DefaultEntitlementService.java
index 8369a8a..d74b8be 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/DefaultEntitlementService.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/DefaultEntitlementService.java
@@ -107,7 +107,7 @@ public class DefaultEntitlementService implements EntitlementService {
                     } else if (inputKey instanceof BlockingTransitionNotificationKey) {
                         processBlockingNotification((BlockingTransitionNotificationKey) inputKey, internalCallContext);
                     } else if (inputKey != null) {
-                        log.error("Entitlement service received an unexpected event type {}" + inputKey.getClass());
+                        log.error("Entitlement service received an unexpected event className='{}", inputKey.getClass());
                     } else {
                         log.error("Entitlement service received an unexpected null event");
                     }
@@ -127,12 +127,12 @@ public class DefaultEntitlementService implements EntitlementService {
         try {
             entitlement = entitlementInternalApi.getEntitlementForId(key.getEntitlementId(), internalCallContext);
         } catch (final EntitlementApiException e) {
-            log.error("Error retrieving entitlement for id " + key.getEntitlementId(), e);
+            log.error("Error retrieving entitlementId='{}'", key.getEntitlementId(), e);
             return;
         }
 
         if (!(entitlement instanceof DefaultEntitlement)) {
-            log.error("Entitlement service received an unexpected entitlement class type {}" + entitlement.getClass().getName());
+            log.error("Error retrieving entitlementId='{}', unexpected entitlement className='{}'", key.getEntitlementId(), entitlement.getClass().getName());
             return;
         }
 
@@ -147,7 +147,7 @@ public class DefaultEntitlementService implements EntitlementService {
                 entitlementInternalApi.resume(key.getBundleId(), internalCallContext.toLocalDate(key.getEffectiveDate(), ((DefaultEntitlement) entitlement).getSubscriptionBase().getStartDate()), ImmutableList.<PluginProperty>of(), internalCallContext);
             }
         } catch (final EntitlementApiException e) {
-            log.error("Error processing event for entitlement {}" + entitlement.getId(), e);
+            log.error("Error processing event for entitlementId='{}'", entitlement.getId(), e);
         }
     }
 
@@ -190,7 +190,7 @@ public class DefaultEntitlementService implements EntitlementService {
         try {
             eventBus.post(event);
         } catch (final EventBusException e) {
-            log.warn("Failed to post event {}", e);
+            log.warn("Failed to post event {}", event, e);
         }
     }
 
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/provider/DefaultEntitlementProviderPluginRegistry.java b/entitlement/src/main/java/org/killbill/billing/entitlement/provider/DefaultEntitlementProviderPluginRegistry.java
index 54b1f19..9f0b11a 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/provider/DefaultEntitlementProviderPluginRegistry.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/provider/DefaultEntitlementProviderPluginRegistry.java
@@ -41,13 +41,13 @@ public class DefaultEntitlementProviderPluginRegistry implements OSGIServiceRegi
 
     @Override
     public void registerService(final OSGIServiceDescriptor desc, final EntitlementPluginApi service) {
-        log.info("DefaultEntitlementProviderPluginRegistry registering service " + desc.getRegistrationName());
+        log.info("Registering service='{}'", desc.getRegistrationName());
         pluginsByName.put(desc.getRegistrationName(), service);
     }
 
     @Override
     public void unregisterService(final String serviceName) {
-        log.info("DefaultEntitlementProviderPluginRegistry unregistering service " + serviceName);
+        log.info("Unregistering service='{}'", serviceName);
         pluginsByName.remove(serviceName);
     }
 
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/DefaultInvoiceService.java b/invoice/src/main/java/org/killbill/billing/invoice/api/DefaultInvoiceService.java
index 961d3e7..1c20ed9 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/api/DefaultInvoiceService.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/DefaultInvoiceService.java
@@ -56,7 +56,7 @@ public class DefaultInvoiceService implements InvoiceService {
             eventBus.register(invoiceListener);
             eventBus.register(tagHandler);
         } catch (PersistentBus.EventBusException e) {
-            throw new RuntimeException("Unable to register to the EventBus!", e);
+            throw new RuntimeException("Failed to register bus handlers", e);
         }
         dateNotifier.initialize();
     }
@@ -72,7 +72,7 @@ public class DefaultInvoiceService implements InvoiceService {
             eventBus.unregister(invoiceListener);
             eventBus.unregister(tagHandler);
         } catch (PersistentBus.EventBusException e) {
-            throw new RuntimeException("Unable to unregister to the EventBus!", e);
+            throw new RuntimeException("Failed to unregister bus handlers", e);
         }
         dateNotifier.stop();
     }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/InvoiceApiHelper.java b/invoice/src/main/java/org/killbill/billing/invoice/api/InvoiceApiHelper.java
index 8a5933b..2b02a73 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/api/InvoiceApiHelper.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/InvoiceApiHelper.java
@@ -103,7 +103,7 @@ public class InvoiceApiHelper {
             final List<InvoiceItemModelDao> createdInvoiceItems = dao.createInvoices(invoiceModelDaos, internalCallContext);
             return fromInvoiceItemModelDao(createdInvoiceItems);
         } catch (final LockFailedException e) {
-            log.error(String.format("Failed to process invoice items for account %s", accountId.toString()), e);
+            log.warn("Failed to process invoice items for accountId='{}'", accountId.toString(), e);
             return ImmutableList.<InvoiceItem>of();
         } finally {
             if (lock != null) {
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java b/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
index 7e7406c..c7ba438 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
@@ -456,10 +456,11 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
     }
 
     private void notifyBusOfInvoiceAdjustment(final UUID invoiceId, final UUID accountId, final InternalCallContext context) {
+        final DefaultInvoiceAdjustmentEvent event = new DefaultInvoiceAdjustmentEvent(invoiceId, accountId, context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken());
         try {
-            eventBus.post(new DefaultInvoiceAdjustmentEvent(invoiceId, accountId, context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken()));
+            eventBus.post(event);
         } catch (final EventBusException e) {
-            log.warn("Failed to post adjustment event for invoice " + invoiceId, e);
+            log.warn("Failed to post event {}", event, e);
         }
     }
 
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
index bac4b81..f12284a 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
@@ -877,7 +877,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
             eventBus.postFromTransaction(new DefaultInvoiceAdjustmentEvent(invoiceId, accountId, context.getAccountRecordId(), context.getTenantRecordId(), userToken),
                                          entitySqlDaoWrapperFactory.getHandle().getConnection());
         } catch (final EventBusException e) {
-            log.warn("Failed to post adjustment event for invoice " + invoiceId, e);
+            log.warn("Failed to post adjustment event for invoiceId='{}'", invoiceId, e);
         }
     }
 
@@ -916,7 +916,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
         try {
             eventBus.postFromTransaction(busEvent, entitySqlDaoWrapperFactory.getHandle().getConnection());
         } catch (final EventBusException e) {
-            log.warn("Failed to post invoice payment event for invoice " + invoicePaymentModelDao.getInvoiceId(), e);
+            log.warn("Failed to post invoice payment event for invoiceId='{}'", invoicePaymentModelDao.getInvoiceId(), e);
         }
     }
 
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/generator/FixedAndRecurringInvoiceItemGenerator.java b/invoice/src/main/java/org/killbill/billing/invoice/generator/FixedAndRecurringInvoiceItemGenerator.java
index 27e6a43..f024a1b 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/generator/FixedAndRecurringInvoiceItemGenerator.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/generator/FixedAndRecurringInvoiceItemGenerator.java
@@ -103,10 +103,11 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator 
         }
 
         // Pretty-print the generated invoice items from the junction events
-        final StringBuilder logStringBuilder = new StringBuilder("Proposed Invoice items for invoiceId ")
+        final StringBuilder logStringBuilder = new StringBuilder("Proposed Invoice items for invoiceId='")
                 .append(invoiceId)
-                .append(" and accountId ")
-                .append(accountId);
+                .append("', accountId='")
+                .append(accountId)
+                .append("'");
 
         final Iterator<BillingEvent> eventIt = events.iterator();
         BillingEvent nextEvent = eventIt.next();
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
index cf7c200..d46a0ed 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
@@ -182,7 +182,7 @@ public class InvoiceDispatcher {
             try {
                 eventBus.post(event);
             } catch (EventBusException e) {
-                log.error("Failed to post event " + event, e);
+                log.warn("Failed to post event {}", event, e);
             }
         }
     }
@@ -190,7 +190,7 @@ public class InvoiceDispatcher {
     private Invoice processSubscriptionInternal(final UUID subscriptionId, final DateTime targetDate, final boolean dryRunForNotification, final InternalCallContext context) throws InvoiceApiException {
         try {
             if (subscriptionId == null) {
-                log.error("Failed handling SubscriptionBase change.", new InvoiceApiException(ErrorCode.INVOICE_INVALID_TRANSITION));
+                log.warn("Failed handling SubscriptionBase change.", new InvoiceApiException(ErrorCode.INVOICE_INVALID_TRANSITION));
                 return null;
             }
             final UUID accountId = subscriptionApi.getAccountIdFromSubscriptionId(subscriptionId, context);
@@ -198,7 +198,7 @@ public class InvoiceDispatcher {
 
             return processAccount(accountId, targetDate, dryRunArguments, context);
         } catch (final SubscriptionBaseApiException e) {
-            log.error("Failed handling SubscriptionBase change.",
+            log.warn("Failed handling SubscriptionBase change.",
                       new InvoiceApiException(ErrorCode.INVOICE_NO_ACCOUNT_ID_FOR_SUBSCRIPTION_ID, subscriptionId.toString()));
             return null;
         }
@@ -212,9 +212,7 @@ public class InvoiceDispatcher {
 
             return processAccountWithLock(accountId, targetDate, dryRunArguments, context);
         } catch (final LockFailedException e) {
-            // Not good!
-            log.error(String.format("Failed to process invoice for account %s, targetDate %s",
-                                    accountId.toString(), targetDate), e);
+            log.warn("Failed to process invoice for accountId='{}', targetDate='{}'", accountId.toString(), targetDate, e);
         } finally {
             if (lock != null) {
                 lock.release();
@@ -324,15 +322,15 @@ public class InvoiceDispatcher {
             //
             if (invoice == null) {
                 if (isDryRun) {
-                    log.info("Generated null dryRun invoice for accountId {} and targetDate {} (targetDateTime {})", new Object[]{accountId, targetDate, targetDateTime});
+                    log.info("Generated null dryRun invoice for accountId='{}', targetDate='{}', targetDateTime='{}'", accountId, targetDate, targetDateTime);
                 } else {
-                    log.info("Generated null invoice for accountId {} and targetDate {} (targetDateTime {})", new Object[]{accountId, targetDate, targetDateTime});
+                    log.info("Generated null invoice for accountId='{}', targetDate='{}', targetDateTime='{}'", accountId, targetDate, targetDateTime);
 
                     final BusInternalEvent event = new DefaultNullInvoiceEvent(accountId, clock.getUTCToday(),
                                                                                context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken());
 
                     commitInvoiceAndSetFutureNotifications(account, null, ImmutableList.<InvoiceItemModelDao>of(), futureAccountNotifications, false, context);
-                    postEvent(event, accountId, context);
+                    postEvent(event);
                 }
                 return null;
             }
@@ -451,10 +449,10 @@ public class InvoiceDispatcher {
     private void logInvoiceWithItems(final ImmutableAccountData account, final Invoice invoice, final LocalDate targetDate, final Set<UUID> adjustedUniqueOtherInvoiceId, final boolean isRealInvoiceWithItems) {
         final StringBuilder tmp = new StringBuilder();
         if (isRealInvoiceWithItems) {
-            tmp.append(String.format("Generated invoice %s with %d items for accountId %s and targetDate %s:\n", invoice.getId(), invoice.getNumberOfItems(), account.getId(), targetDate));
+            tmp.append(String.format("Generated invoiceId='%s', numberOfItems='%d', accountId='%s', targetDate='%s':\n", invoice.getId(), invoice.getNumberOfItems(), account.getId(), targetDate));
         } else {
             final String adjustedInvoices = JOINER_COMMA.join(adjustedUniqueOtherInvoiceId.toArray(new UUID[adjustedUniqueOtherInvoiceId.size()]));
-            tmp.append(String.format("Adjusting existing invoices %s with %d items for accountId %s and targetDate %s:\n",
+            tmp.append(String.format("Adjusting existing invoiceId='%s', numberOfItems='%d', accountId='%s', targetDate='%s':\n",
                                      adjustedInvoices, invoice.getNumberOfItems(), account.getId(), targetDate));
         }
         for (InvoiceItem item : invoice.getInvoiceItems()) {
@@ -499,7 +497,7 @@ public class InvoiceDispatcher {
             events.add(event);
         }
         for (final InvoiceInternalEvent event : events) {
-            postEvent(event, account.getId(), context);
+            postEvent(event);
         }
     }
 
@@ -554,11 +552,11 @@ public class InvoiceDispatcher {
         }
     }
 
-    private void postEvent(final BusInternalEvent event, final UUID accountId, final InternalCallContext context) {
+    private void postEvent(final BusInternalEvent event) {
         try {
             eventBus.post(event);
         } catch (final EventBusException e) {
-            log.error(String.format("Failed to post event %s for account %s", event.getBusEventType(), accountId), e);
+            log.warn("Failed to post event {}", event, e);
         }
     }
 
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceListener.java b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceListener.java
index 069013d..0a82d37 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceListener.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceListener.java
@@ -74,7 +74,7 @@ public class InvoiceListener {
             final InternalCallContext context = internalCallContextFactory.createInternalCallContext(event.getSearchKey2(), event.getSearchKey1(), "SubscriptionBaseTransition", CallOrigin.INTERNAL, UserType.SYSTEM, event.getUserToken());
             dispatcher.processSubscriptionForInvoiceGeneration(event, context);
         } catch (InvoiceApiException e) {
-            log.error(e.getMessage());
+            log.warn("Unable to process event {}", event, e);
         }
     }
 
@@ -86,7 +86,7 @@ public class InvoiceListener {
             final InternalCallContext context = internalCallContextFactory.createInternalCallContext(event.getSearchKey2(), event.getSearchKey1(), "SubscriptionBaseTransition", CallOrigin.INTERNAL, UserType.SYSTEM, event.getUserToken());
             dispatcher.processAccount(event.getAccountId(), event.getEffectiveTransitionTime(), null, context);
         } catch (InvoiceApiException e) {
-            log.error(e.getMessage());
+            log.warn("Unable to process event {}", event, e);
         }
     }
 
@@ -104,9 +104,9 @@ public class InvoiceListener {
             final UUID accountId = accountApi.getByRecordId(event.getSearchKey1(), context);
             dispatcher.processAccount(accountId, clock.getUTCNow(), null, context);
         } catch (InvoiceApiException e) {
-            log.error(e.getMessage());
+            log.warn("Unable to process event {}", event, e);
         } catch (AccountApiException e) {
-            log.error(e.getMessage());
+            log.warn("Unable to process event {}", event, e);
         }
     }
 
@@ -115,7 +115,7 @@ public class InvoiceListener {
             final InternalCallContext context = internalCallContextFactory.createInternalCallContext(tenantRecordId, accountRecordId, "Next Billing Date", CallOrigin.INTERNAL, UserType.SYSTEM, userToken);
             dispatcher.processSubscriptionForInvoiceGeneration(subscriptionId, eventDateTime, context);
         } catch (InvoiceApiException e) {
-            log.error(e.getMessage());
+            log.warn("Unable to process subscriptionId='{}', eventDateTime='{}'", subscriptionId, eventDateTime, e);
         }
     }
 
@@ -124,7 +124,7 @@ public class InvoiceListener {
             final InternalCallContext context = internalCallContextFactory.createInternalCallContext(tenantRecordId, accountRecordId, "Next Billing Date", CallOrigin.INTERNAL, UserType.SYSTEM, userToken);
             dispatcher.processSubscriptionForInvoiceNotification(subscriptionId, eventDateTime, context);
         } catch (InvoiceApiException e) {
-            log.error(e.getMessage());
+            log.warn("Unable to process subscriptionId='{}', eventDateTime='{}'", subscriptionId, eventDateTime, e);
         }
     }
 }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceTagHandler.java b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceTagHandler.java
index cb6ed8c..facb7ba 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceTagHandler.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceTagHandler.java
@@ -67,7 +67,7 @@ public class InvoiceTagHandler {
         try {
             dispatcher.processAccount(accountId, clock.getUTCNow(), null, context);
         } catch (InvoiceApiException e) {
-            log.warn(String.format("Failed to process process removal AUTO_INVOICING_OFF for account %s", accountId), e);
+            log.warn("Failed to process tag removal AUTO_INVOICING_OFF for accountId='{}'", accountId, e);
         }
     }
 }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/notification/DefaultNextBillingDateNotifier.java b/invoice/src/main/java/org/killbill/billing/invoice/notification/DefaultNextBillingDateNotifier.java
index e5ec2ae..05e661f 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/notification/DefaultNextBillingDateNotifier.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/notification/DefaultNextBillingDateNotifier.java
@@ -70,33 +70,29 @@ public class DefaultNextBillingDateNotifier implements NextBillingDateNotifier {
         final NotificationQueueHandler notificationQueueHandler = new NotificationQueueHandler() {
             @Override
             public void handleReadyNotification(final NotificationEvent notificationKey, final DateTime eventDate, final UUID userToken, final Long accountRecordId, final Long tenantRecordId) {
+                if (!(notificationKey instanceof NextBillingDateNotificationKey)) {
+                    log.error("Invoice service received an unexpected event className='{}", notificationKey.getClass());
+                    return;
+                }
+
+                final NextBillingDateNotificationKey key = (NextBillingDateNotificationKey) notificationKey;
+
+                // Just to ensure compatibility with json that might not have that targetDate field (old versions < 0.13.6)
+                final DateTime targetDate = key.getTargetDate() != null ? key.getTargetDate() : eventDate;
                 try {
-                    if (!(notificationKey instanceof NextBillingDateNotificationKey)) {
-                        log.error("Invoice service received an unexpected event type {}", notificationKey.getClass().getName());
+                    final SubscriptionBase subscription = subscriptionApi.getSubscriptionFromId(key.getUuidKey(), callContextFactory.createInternalTenantContext(tenantRecordId, accountRecordId));
+                    if (subscription == null) {
+                        log.warn("Unable to retrieve subscriptionId='{}' for event {}", key.getUuidKey(), key);
                         return;
                     }
-
-                    final NextBillingDateNotificationKey key = (NextBillingDateNotificationKey) notificationKey;
-
-                    // Just to ensure compatibility with json that might not have that targetDate field (old versions < 0.13.6)
-                    final DateTime targetDate = key.getTargetDate() != null ? key.getTargetDate() : eventDate;
-                    try {
-                        final SubscriptionBase subscription = subscriptionApi.getSubscriptionFromId(key.getUuidKey(), callContextFactory.createInternalTenantContext(tenantRecordId, accountRecordId));
-                        if (subscription == null) {
-                            log.warn("Next Billing Date Notification Queue handled spurious notification (key: " + key + ")");
-                            return;
-                        }
-                        if (key.isDryRunForInvoiceNotification() != null && // Just to ensure compatibility with json that might not have that field (old versions < 0.13.6)
-                            key.isDryRunForInvoiceNotification()) {
-                            processEventForInvoiceNotification(key.getUuidKey(), targetDate, userToken, accountRecordId, tenantRecordId);
-                        } else {
-                            processEventForInvoiceGeneration(key.getUuidKey(), targetDate, userToken, accountRecordId, tenantRecordId);
-                        }
-                    } catch (SubscriptionBaseApiException e) {
-                        log.warn("Next Billing Date Notification Queue handled spurious notification (key: " + key + ")", e);
+                    if (key.isDryRunForInvoiceNotification() != null && // Just to ensure compatibility with json that might not have that field (old versions < 0.13.6)
+                        key.isDryRunForInvoiceNotification()) {
+                        processEventForInvoiceNotification(key.getUuidKey(), targetDate, userToken, accountRecordId, tenantRecordId);
+                    } else {
+                        processEventForInvoiceGeneration(key.getUuidKey(), targetDate, userToken, accountRecordId, tenantRecordId);
                     }
-                } catch (IllegalArgumentException e) {
-                    log.error("The key returned from the NextBillingNotificationQueue is not a valid UUID", e);
+                } catch (SubscriptionBaseApiException e) {
+                    log.warn("Error retrieving subscriptionId='{}'", key.getUuidKey(), e);
                 }
             }
         };
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/provider/DefaultInvoiceProviderPluginRegistry.java b/invoice/src/main/java/org/killbill/billing/invoice/provider/DefaultInvoiceProviderPluginRegistry.java
index 404b9fc..1860257 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/provider/DefaultInvoiceProviderPluginRegistry.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/provider/DefaultInvoiceProviderPluginRegistry.java
@@ -41,13 +41,13 @@ public class DefaultInvoiceProviderPluginRegistry implements OSGIServiceRegistra
 
     @Override
     public void registerService(final OSGIServiceDescriptor desc, final InvoicePluginApi service) {
-        log.info("DefaultInvoiceProviderPluginRegistry registering service " + desc.getRegistrationName());
+        log.info("Registering service='{}'", desc.getRegistrationName());
         pluginsByName.put(desc.getRegistrationName(), service);
     }
 
     @Override
     public void unregisterService(final String serviceName) {
-        log.info("DefaultInvoiceProviderPluginRegistry unregistering service " + serviceName);
+        log.info("Unregistering service='{}'", serviceName);
         pluginsByName.remove(serviceName);
     }
 
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceFormatter.java b/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceFormatter.java
index 7ebb8d2..6f38c8f 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceFormatter.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceFormatter.java
@@ -284,10 +284,10 @@ public class DefaultInvoiceFormatter implements InvoiceFormatter {
                 }
             }
         } catch (final CurrencyConversionException e) {
-            logger.warn("Failed to retrieve currency conversion rates for currency = " + currency + " and date = " + latestPaymentDate, e);
+            logger.warn("Failed to retrieve currency conversion rates for currency='{}', dateConversion='{}'", currency, latestPaymentDate, e);
             return null;
         }
-        logger.warn("Failed to retrieve currency conversion rates for currency = " + currency + " and date = " + latestPaymentDate);
+        logger.warn("Failed to retrieve currency conversion rates for currency='{}', dateConversion='{}'", currency, latestPaymentDate);
         return null;
     }
 
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/usage/RawUsageOptimizer.java b/invoice/src/main/java/org/killbill/billing/invoice/usage/RawUsageOptimizer.java
index afdc98d..b78fcbc 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/usage/RawUsageOptimizer.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/usage/RawUsageOptimizer.java
@@ -67,8 +67,8 @@ public class RawUsageOptimizer {
 
     public RawUsageOptimizerResult getConsumableInArrearUsage(final LocalDate firstEventStartDate, final LocalDate targetDate, final Iterable<InvoiceItem> existingUsageItems, final Map<String, Usage> knownUsage, final InternalCallContext internalCallContext) {
         final LocalDate targetStartDate = config.getMaxRawUsagePreviousPeriod() > 0 ? getOptimizedRawUsageStartDate(firstEventStartDate, targetDate, existingUsageItems, knownUsage) : firstEventStartDate;
-        log.info("RawUsageOptimizer [accountRecordId = {}]: rawUsageStartDate = {}, (proposed) firstEventStartDate = {}",
-                 new Object[]{internalCallContext.getAccountRecordId(), targetStartDate, firstEventStartDate});
+        log.info("ConsumableInArrear accountRecordId='{}', rawUsageStartDate='{}', firstEventStartDate='{}'",
+                 internalCallContext.getAccountRecordId(), targetStartDate, firstEventStartDate);
 
         final List<RawUsage> rawUsageData = usageApi.getRawUsageForAccount(targetStartDate, targetDate, internalCallContext);
         return new RawUsageOptimizerResult(firstEventStartDate, targetStartDate, rawUsageData);
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/mappers/ExceptionMapperBase.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/mappers/ExceptionMapperBase.java
index 8794159..35cd19f 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/mappers/ExceptionMapperBase.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/mappers/ExceptionMapperBase.java
@@ -1,7 +1,9 @@
 /*
- * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2010-2014 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
  * License.  You may obtain a copy of the License at:
  *
@@ -112,55 +114,40 @@ public abstract class ExceptionMapperBase {
     }
 
     protected Response buildConflictingRequestResponse(final Exception e, final UriInfo uriInfo) {
-        // Log the full stacktrace
-        log.warn("Conflicting request", e);
-
         final Response.ResponseBuilder responseBuilder = Response.status(Status.CONFLICT);
         serializeException(e, uriInfo, responseBuilder);
-        return responseBuilder.build();
+        return new LoggingResponse(e, responseBuilder.build());
     }
 
     protected Response buildNotFoundResponse(final Exception e, final UriInfo uriInfo) {
-        // Log the full stacktrace
-        log.info("Not found", e);
-
         final Response.ResponseBuilder responseBuilder = Response.status(Status.NOT_FOUND);
         serializeException(e, uriInfo, responseBuilder);
-        return responseBuilder.build();
+        return new LoggingResponse(e, responseBuilder.build());
     }
 
     protected Response buildBadRequestResponse(final Exception e, final UriInfo uriInfo) {
-        // Log the full stacktrace
-        log.warn("Bad request", e);
-
         final Response.ResponseBuilder responseBuilder = Response.status(Status.BAD_REQUEST);
         serializeException(e, uriInfo, responseBuilder);
-        return responseBuilder.build();
+        return new LoggingResponse(e, responseBuilder.build());
     }
 
     protected Response buildAuthorizationErrorResponse(final Exception e, final UriInfo uriInfo) {
-        // Log the full stacktrace
-        log.warn("Authorization error", e);
-
         // TODO Forbidden?
         final Response.ResponseBuilder responseBuilder = Response.status(Status.UNAUTHORIZED);
         serializeException(e, uriInfo, responseBuilder);
-        return responseBuilder.build();
+        return new LoggingResponse(e, responseBuilder.build());
     }
 
     protected Response buildInternalErrorResponse(final Exception e, final UriInfo uriInfo) {
-        // Log the full stacktrace
-        log.warn("Internal error", e);
-
         final Response.ResponseBuilder responseBuilder = Response.status(Status.INTERNAL_SERVER_ERROR);
         serializeException(e, uriInfo, responseBuilder);
-        return responseBuilder.build();
+        return new LoggingResponse(e, responseBuilder.build());
     }
 
     protected Response buildPluginTimeoutResponse(final Exception e, final UriInfo uriInfo) {
         final Response.ResponseBuilder responseBuilder = Response.status(Status.ACCEPTED);
         serializeException(e, uriInfo, responseBuilder);
-        return responseBuilder.build();
+        return new LoggingResponse(e, responseBuilder.build());
     }
 
     private void serializeException(final Exception e, final UriInfo uriInfo, final Response.ResponseBuilder responseBuilder) {
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/mappers/LoggingResponse.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/mappers/LoggingResponse.java
new file mode 100644
index 0000000..e3635ca
--- /dev/null
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/mappers/LoggingResponse.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2016 Groupon, Inc
+ * Copyright 2016 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.jaxrs.mappers;
+
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class LoggingResponse extends Response {
+
+    private static final Logger log = LoggerFactory.getLogger(LoggingResponse.class);
+
+    private final Exception e;
+    private final Response response;
+
+    public LoggingResponse(final Exception e, final Response response) {
+        this.e = e;
+        this.response = response;
+    }
+
+    @Override
+    public Object getEntity() {
+        // Delay logging until the entity is retrieved: this is to avoid double logging with TimedResourceInterceptor
+        // which needs to access exception mappers to get the response status
+        if (response.getStatus() == Status.CONFLICT.getStatusCode()) {
+            log.warn("Conflicting request", e);
+        } else if (response.getStatus() == Status.NOT_FOUND.getStatusCode()) {
+            log.debug("Not found", e);
+        } else if (response.getStatus() == Status.BAD_REQUEST.getStatusCode()) {
+            log.warn("Bad request", e);
+        } else if (response.getStatus() == Status.UNAUTHORIZED.getStatusCode()) {
+            log.debug("Authorization error", e);
+        } else if (response.getStatus() == Status.INTERNAL_SERVER_ERROR.getStatusCode()) {
+            log.warn("Internal error", e);
+        }
+
+        return response.getEntity();
+    }
+
+    @Override
+    public int getStatus() {
+        return response.getStatus();
+    }
+
+    @Override
+    public MultivaluedMap<String, Object> getMetadata() {
+        return response.getMetadata();
+    }
+}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentMethodResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentMethodResource.java
index 07fdd5e..731db34 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentMethodResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentMethodResource.java
@@ -179,7 +179,7 @@ public class PaymentMethodResource extends JaxRsResourceBase {
                                                                 account = accountUserApi.getAccountById(paymentMethod.getAccountId(), tenantContext);
                                                                 accounts.put(paymentMethod.getAccountId(), account);
                                                             } catch (final AccountApiException e) {
-                                                                log.warn("Unable to retrieve account", e);
+                                                                log.warn("Error retrieving accountId='{}'", paymentMethod.getAccountId(), e);
                                                                 return null;
                                                             }
                                                         }
@@ -238,7 +238,7 @@ public class PaymentMethodResource extends JaxRsResourceBase {
                                                                 account = accountUserApi.getAccountById(paymentMethod.getAccountId(), tenantContext);
                                                                 accounts.put(paymentMethod.getAccountId(), account);
                                                             } catch (final AccountApiException e) {
-                                                                log.warn("Unable to retrieve account", e);
+                                                                log.warn("Error retrieving accountId='{}'", paymentMethod.getAccountId(), e);
                                                                 return null;
                                                             }
                                                         }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
index 7749b79..62ddc79 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
@@ -92,7 +92,6 @@ import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
-import com.google.common.base.Strings;
 import com.google.common.collect.Iterables;
 import com.google.inject.Inject;
 import com.wordnik.swagger.annotations.Api;
@@ -515,20 +514,19 @@ public class SubscriptionResource extends JaxRsResourceBase {
         @Override
         public void onSubscriptionBaseTransition(final EffectiveSubscriptionInternalEvent event) {
 
-            log.info(String.format("Got event SubscriptionBaseTransition token = %s, type = %s, remaining = %d ",
-                                   event.getUserToken(), event.getTransitionType(), event.getRemainingEventsForUserOperation()));
+            log.info("Got event SubscriptionBaseTransition token='{}', type='{}', remaining='{}'", event.getUserToken(), event.getTransitionType(), event.getRemainingEventsForUserOperation());
         }
 
         @Override
         public void onEmptyInvoice(final NullInvoiceInternalEvent event) {
-            log.info(String.format("Got event EmptyInvoiceNotification token = %s ", event.getUserToken()));
+            log.info("Got event EmptyInvoiceNotification token='{}'", event.getUserToken());
             notifyForCompletion();
         }
 
         @Override
         public void onInvoiceCreation(final InvoiceCreationInternalEvent event) {
 
-            log.info(String.format("Got event InvoiceCreationNotification token = %s ", event.getUserToken()));
+            log.info("Got event InvoiceCreationNotification token='{}'", event.getUserToken());
             if (event.getAmountOwed().compareTo(BigDecimal.ZERO) <= 0) {
                 notifyForCompletion();
             }
@@ -536,19 +534,19 @@ public class SubscriptionResource extends JaxRsResourceBase {
 
         @Override
         public void onPaymentInfo(final PaymentInfoInternalEvent event) {
-            log.info(String.format("Got event PaymentInfo token = %s ", event.getUserToken()));
+            log.info("Got event PaymentInfo token='{}'", event.getUserToken());
             notifyForCompletion();
         }
 
         @Override
         public void onPaymentError(final PaymentErrorInternalEvent event) {
-            log.info(String.format("Got event PaymentError token = %s ", event.getUserToken()));
+            log.info("Got event PaymentError token='{}'", event.getUserToken());
             notifyForCompletion();
         }
 
         @Override
         public void onPaymentPluginError(final PaymentPluginErrorInternalEvent event) {
-            log.info(String.format("Got event PaymentPluginError token = %s ", event.getUserToken()));
+            log.info("Got event PaymentPluginError token='{}'", event.getUserToken());
             notifyForCompletion();
         }
     }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/util/Context.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/util/Context.java
index a30a9d3..d431641 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/util/Context.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/util/Context.java
@@ -1,7 +1,9 @@
 /*
- * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2010-2014 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
  * License.  You may obtain a copy of the License at:
  *
@@ -26,6 +28,7 @@ import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.CallContextFactory;
 import org.killbill.billing.util.callcontext.CallOrigin;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.billing.util.callcontext.UserType;
 import org.killbill.commons.request.Request;
@@ -37,13 +40,15 @@ public class Context {
 
     private final CallOrigin origin;
     private final UserType userType;
-    final CallContextFactory contextFactory;
+    private final CallContextFactory contextFactory;
+    private final InternalCallContextFactory internalCallContextFactory;
 
     @Inject
-    public Context(final CallContextFactory factory) {
+    public Context(final CallContextFactory factory, final InternalCallContextFactory internalCallContextFactory) {
         this.origin = CallOrigin.EXTERNAL;
         this.userType = UserType.CUSTOMER;
         this.contextFactory = factory;
+        this.internalCallContextFactory = internalCallContextFactory;
     }
 
     public CallContext createContext(final String createdBy, final String reason, final String comment, final ServletRequest request)
@@ -51,23 +56,31 @@ public class Context {
         try {
             Preconditions.checkNotNull(createdBy, String.format("Header %s needs to be set", JaxrsResource.HDR_CREATED_BY));
             final Tenant tenant = getTenantFromRequest(request);
+            final CallContext callContext = contextFactory.createCallContext(tenant == null ? null : tenant.getId(), createdBy, origin, userType, reason,
+                                                                             comment, getOrCreateUserToken());
 
+            populateMDCContext(callContext);
 
-            return contextFactory.createCallContext(tenant == null ? null : tenant.getId(), createdBy, origin, userType, reason,
-                                                    comment, getOrCreateUserToken());
+            return callContext;
         } catch (final NullPointerException e) {
             throw new IllegalArgumentException(e.getMessage());
         }
     }
 
     public TenantContext createContext(final ServletRequest request) {
+        final TenantContext tenantContext;
+
         final Tenant tenant = getTenantFromRequest(request);
         if (tenant == null) {
             // Multi-tenancy may not have been configured - default to "default" tenant (see InternalCallContextFactory)
-            return contextFactory.createTenantContext(null);
+            tenantContext = contextFactory.createTenantContext(null);
         } else {
-            return contextFactory.createTenantContext(tenant.getId());
+            tenantContext = contextFactory.createTenantContext(tenant.getId());
         }
+
+        populateMDCContext(tenantContext);
+
+        return tenantContext;
     }
 
     // Use REQUEST_ID_HEADER if this is provided and lloks like a UUID, if not allocate a random one.
@@ -94,4 +107,9 @@ public class Context {
             return (Tenant) tenantObject;
         }
     }
+
+    private void populateMDCContext(final TenantContext tenantContext) {
+        // InternalCallContextFactory will do it for us
+        internalCallContextFactory.createInternalTenantContext(tenantContext);
+    }
 }
diff --git a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BillCycleDayCalculator.java b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BillCycleDayCalculator.java
index 84b3bc1..733f876 100644
--- a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BillCycleDayCalculator.java
+++ b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BillCycleDayCalculator.java
@@ -144,7 +144,7 @@ public class BillCycleDayCalculator {
 
         final DateTime date = plan.dateOfFirstRecurringNonZeroCharge(subscription.getStartDate(), initialPhaseType);
         final int bcdLocal = context.toDateTime(date, account.getTimeZone()).getDayOfMonth();
-        log.info("Calculated BCD: subscription id {}, subscription start {}, timezone {}, bcd {}",
+        log.info("Calculated BCD: subscriptionId='{}', subscriptionStartDate='{}', accountTimeZone='{}', bcd='{}'",
                  subscription.getId(), date.toDateTimeISO(), account.getTimeZone(), bcdLocal);
 
         return bcdLocal;
diff --git a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
index 359975f..f263960 100644
--- a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
+++ b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
@@ -110,7 +110,7 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
         }
 
         // Pretty-print the events, before and after the blocking calculator does its magic
-        final StringBuilder logStringBuilder = new StringBuilder("Computed billing events for accountId ").append(accountId);
+        final StringBuilder logStringBuilder = new StringBuilder("Computed billing events for accountId='").append(accountId).append("'");
         eventsToString(logStringBuilder, result, "\nBilling Events Raw");
         blockCalculator.insertBlockingEvents(result, skippedSubscriptions, context);
         eventsToString(logStringBuilder, result, "\nBilling Events After Blocking");
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java b/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java
index 91a0e03..22d0865 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java
@@ -27,7 +27,6 @@ import java.util.UUID;
 import javax.inject.Named;
 
 import org.joda.time.DateTime;
-import org.joda.time.LocalDate;
 import org.joda.time.Period;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.ObjectType;
@@ -171,11 +170,20 @@ public class OverdueStateApplicator {
         } catch (final AccountApiException e) {
             throw new OverdueException(e);
         }
+
+        final OverdueChangeInternalEvent event;
+        try {
+            event = createOverdueEvent(account, previousOverdueState.getName(), nextOverdueState.getName(), isBlockBillingTransition(previousOverdueState, nextOverdueState),
+                                       isUnblockBillingTransition(previousOverdueState, nextOverdueState), context);
+        } catch (final BlockingApiException e) {
+            log.warn("Failed to create OverdueChangeInternalEvent for accountId='{}'", account.getId(), e);
+            return;
+        }
+
         try {
-            bus.post(createOverdueEvent(account, previousOverdueState.getName(), nextOverdueState.getName(), isBlockBillingTransition(previousOverdueState, nextOverdueState),
-                                        isUnblockBillingTransition(previousOverdueState, nextOverdueState), context));
+            bus.post(event);
         } catch (final Exception e) {
-            log.error("Error posting overdue change event to bus", e);
+            log.warn("Failed to post event {}", event, e);
         }
     }
 
@@ -218,11 +226,19 @@ public class OverdueStateApplicator {
             throw new OverdueException(e);
         }
 
+        final OverdueChangeInternalEvent event;
+        try {
+            event = createOverdueEvent(account, previousOverdueState.getName(), clearState.getName(), isBlockBillingTransition(previousOverdueState, clearState),
+                                       isUnblockBillingTransition(previousOverdueState, clearState), context);
+        } catch (final BlockingApiException e) {
+            log.warn("Failed to create OverdueChangeInternalEvent for accountId='{}'", account.getId(), e);
+            return;
+        }
+
         try {
-            bus.post(createOverdueEvent(account, previousOverdueState.getName(), clearState.getName(), isBlockBillingTransition(previousOverdueState, clearState),
-                                        isUnblockBillingTransition(previousOverdueState, clearState), context));
+            bus.post(event);
         } catch (final Exception e) {
-            log.error("Error posting overdue change event to bus", e);
+            log.warn("Failed to post event {}", event, e);
         }
     }
 
@@ -372,11 +388,11 @@ public class OverdueStateApplicator {
                 emailSender.sendPlainTextEmail(to, cc, subject, emailBody);
             }
         } catch (final IOException e) {
-            log.warn(String.format("Unable to generate or send overdue notification email for account %s and overdueable %s", account.getId(), account.getId()), e);
+            log.warn("Unable to generate or send overdue notification email for accountId='{}'", account.getId(), e);
         } catch (final EmailApiException e) {
-            log.warn(String.format("Unable to send overdue notification email for account %s and overdueable %s", account.getId(), account.getId()), e);
+            log.warn("Unable to send overdue notification email for accountId='{}'", account.getId(), e);
         } catch (final MustacheException e) {
-            log.warn(String.format("Unable to generate overdue notification email for account %s and overdueable %s", account.getId(), account.getId()), e);
+            log.warn("Unable to generate overdue notification email for accountId='{}'", account.getId(), e);
         }
     }
 
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/caching/EhCacheOverdueConfigCache.java b/overdue/src/main/java/org/killbill/billing/overdue/caching/EhCacheOverdueConfigCache.java
index d52470c..7285e87 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/caching/EhCacheOverdueConfigCache.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/caching/EhCacheOverdueConfigCache.java
@@ -60,7 +60,7 @@ public class EhCacheOverdueConfigCache implements OverdueConfigCache {
             defaultOverdueConfig = XMLLoader.getObjectFromUri(noOverdueConfigURI, DefaultOverdueConfig.class);
         } catch (final Exception e) {
             defaultOverdueConfig = new DefaultOverdueConfig();
-            log.warn("Exception loading NoOverdueConfig - should never happen!", e);
+            log.error("Exception loading NoOverdueConfig - should never happen!", e);
         }
     }
 
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/caching/OverdueCacheInvalidationCallback.java b/overdue/src/main/java/org/killbill/billing/overdue/caching/OverdueCacheInvalidationCallback.java
index e7fb85e..4a19678 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/caching/OverdueCacheInvalidationCallback.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/caching/OverdueCacheInvalidationCallback.java
@@ -38,7 +38,7 @@ public class OverdueCacheInvalidationCallback implements CacheInvalidationCallba
 
     @Override
     public void invalidateCache(TenantKey key, final Object cookie, final InternalTenantContext tenantContext) {
-        log.info("Invalidate overdue cache for tenant {} ", tenantContext.getTenantRecordId());
+        log.info("Invalidate overdue cache for tenantRecordId='{}'", tenantContext.getTenantRecordId());
         overdueConfigCache.clearOverdueConfig(tenantContext);
     }
 }
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/listener/OverdueDispatcher.java b/overdue/src/main/java/org/killbill/billing/overdue/listener/OverdueDispatcher.java
index 58653d2..c631b72 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/listener/OverdueDispatcher.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/listener/OverdueDispatcher.java
@@ -50,7 +50,7 @@ public class OverdueDispatcher {
         try {
             factory.createOverdueWrapperFor(accountId, context).refresh(context);
         } catch (BillingExceptionBase e) {
-            log.error(String.format("Error processing Overdue for blockable %s", accountId), e);
+            log.warn("Error processing Overdue for accountId='{}'", accountId, e);
         }
     }
 
@@ -58,7 +58,7 @@ public class OverdueDispatcher {
         try {
             factory.createOverdueWrapperFor(accountId, context).clear(context);
         } catch (BillingExceptionBase e) {
-            log.error(String.format("Error processing Overdue for blockable %s (type %s)", accountId), e);
+            log.warn("Error processing Overdue for accountId='{}'", accountId, e);
         }
     }
 }
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/service/DefaultOverdueService.java b/overdue/src/main/java/org/killbill/billing/overdue/service/DefaultOverdueService.java
index 87cc174..7aa0bed 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/service/DefaultOverdueService.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/service/DefaultOverdueService.java
@@ -91,8 +91,7 @@ public class DefaultOverdueService implements OverdueService {
                 overdueConfigCache.loadDefaultOverdueConfig(properties.getConfigURI());
                 isConfigLoaded = true;
             } catch (OverdueApiException e) {
-                log.warn("Overdue system disabled: unable to loadDefaultCatalog the overdue config from " + properties.getConfigURI(), e);
-                e.printStackTrace();
+                log.warn("Overdue system disabled: unable to load the overdue config from uri='{}'", properties.getConfigURI(), e);
             }
         }
     }
@@ -109,7 +108,7 @@ public class DefaultOverdueService implements OverdueService {
         try {
             busService.getBus().register(listener);
         } catch (final EventBusException e) {
-            log.error("Problem encountered registering OverdueListener on the Event Bus", e);
+            log.error("Failed to register OverdueListener", e);
         }
     }
 
@@ -124,7 +123,7 @@ public class DefaultOverdueService implements OverdueService {
         try {
             busService.getBus().unregister(listener);
         } catch (final EventBusException e) {
-            log.error("Problem encountered registering OverdueListener on the Event Bus", e);
+            log.error("Failed to unregister OverdueListener", e);
         }
         checkNotifier.stop();
         asyncNotifier.stop();
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/wrapper/OverdueWrapper.java b/overdue/src/main/java/org/killbill/billing/overdue/wrapper/OverdueWrapper.java
index 95a2b7d..7743e65 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/wrapper/OverdueWrapper.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/wrapper/OverdueWrapper.java
@@ -87,8 +87,7 @@ public class OverdueWrapper {
 
             return refreshWithLock(context);
         } catch (final LockFailedException e) {
-            // Not good!
-            log.error(String.format("Failed to process overdue for account %s", overdueable.getId()), e);
+            log.warn("Failed to process overdue for accountId='{}'", overdueable.getId(), e);
         } finally {
             if (lock != null) {
                 lock.release();
@@ -116,8 +115,7 @@ public class OverdueWrapper {
 
             clearWithLock(context);
         } catch (final LockFailedException e) {
-            // Not good!
-            log.error(String.format("Failed to clear overdue for account %s", overdueable.getId()), e);
+            log.warn("Failed to clear overdue for accountId='{}'", overdueable.getId(), e);
         } finally {
             if (lock != null) {
                 lock.release();
diff --git a/payment/src/main/java/org/killbill/billing/payment/api/DefaultApiBase.java b/payment/src/main/java/org/killbill/billing/payment/api/DefaultApiBase.java
index cd916d9..6ee3eee 100644
--- a/payment/src/main/java/org/killbill/billing/payment/api/DefaultApiBase.java
+++ b/payment/src/main/java/org/killbill/billing/payment/api/DefaultApiBase.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
@@ -32,11 +32,13 @@ import org.killbill.billing.util.config.PaymentConfig;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableList;
 
 public class DefaultApiBase {
 
     private static final Logger log = LoggerFactory.getLogger(DefaultApiBase.class);
+    private static final Joiner JOINER = Joiner.on(",");
 
     private final PaymentConfig paymentConfig;
 
@@ -44,40 +46,68 @@ public class DefaultApiBase {
         this.paymentConfig = paymentConfig;
     }
 
-    protected void logAPICall(final String transactionType, final Account account, final UUID paymentMethodId, @Nullable final UUID paymentId, @Nullable final UUID transactionId, @Nullable final BigDecimal amount, @Nullable final Currency currency, @Nullable final String paymentExternalKey, @Nullable final String paymentTransactionExternalKey) {
+    protected void logAPICall(final String transactionType,
+                              final Account account,
+                              final UUID paymentMethodId,
+                              @Nullable final UUID paymentId,
+                              @Nullable final UUID transactionId,
+                              @Nullable final BigDecimal amount,
+                              @Nullable final Currency currency,
+                              @Nullable final String paymentExternalKey,
+                              @Nullable final String paymentTransactionExternalKey,
+                              @Nullable final TransactionStatus transactionStatus,
+                              @Nullable final List<String> paymentControlPluginNames) {
         if (log.isInfoEnabled()) {
             final StringBuilder logLine = new StringBuilder();
-            logLine.append("PaymentApi : ")
+            logLine.append("PaymentApi: transactionType='")
                    .append(transactionType)
-                   .append(", account = ")
-                   .append(account.getId());
+                   .append("', accountId='")
+                   .append(account.getId())
+                   .append("'");
             if (paymentMethodId != null) {
-                logLine.append(", paymentMethodId = ")
-                       .append(paymentMethodId);
+                logLine.append(", paymentMethodId='")
+                       .append(paymentMethodId)
+                       .append("'");
             }
             if (paymentExternalKey != null) {
-                logLine.append(", paymentExternalKey = ")
-                       .append(paymentExternalKey);
+                logLine.append(", paymentExternalKey='")
+                       .append(paymentExternalKey)
+                       .append("'");
             }
             if (paymentTransactionExternalKey != null) {
-                logLine.append(", paymentTransactionExternalKey = ")
-                       .append(paymentTransactionExternalKey);
+                logLine.append(", paymentTransactionExternalKey='")
+                       .append(paymentTransactionExternalKey)
+                       .append("'");
             }
             if (paymentId != null) {
-                logLine.append(", paymentId = ")
-                       .append(paymentId);
+                logLine.append(", paymentId='")
+                       .append(paymentId)
+                       .append("'");
             }
             if (transactionId != null) {
-                logLine.append(", transactionId = ")
-                       .append(transactionId);
+                logLine.append(", transactionId='")
+                       .append(transactionId)
+                       .append("'");
             }
             if (amount != null) {
-                logLine.append(", amount = ")
-                       .append(amount);
+                logLine.append(", amount='")
+                       .append(amount)
+                       .append("'");
             }
             if (currency != null) {
-                logLine.append(", currency = ")
-                       .append(currency);
+                logLine.append(", currency='")
+                       .append(currency)
+                       .append("'");
+            }
+            if (transactionStatus != null) {
+                logLine.append(", transactionStatus='")
+                       .append(transactionStatus)
+                       .append("'");
+            }
+            if (paymentControlPluginNames != null) {
+                logLine.append(", paymentControlPluginNames='")
+                       .append(JOINER.join(paymentControlPluginNames))
+                       .append("'");
             }
             log.info(logLine.toString());
         }
diff --git a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java
index 0a39175..b8e1e7a 100644
--- a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java
+++ b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java
@@ -42,6 +42,9 @@ import org.killbill.billing.util.entity.Pagination;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+
 public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
 
     private static final boolean SHOULD_LOCK_ACCOUNT = true;
@@ -76,11 +79,27 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
         }
         checkNotNullParameter(properties, "plugin properties");
 
-        logAPICall(TransactionType.AUTHORIZE.name(), account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey);
+        final String transactionType = TransactionType.AUTHORIZE.name();
+        logAPICall(transactionType, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, null, null);
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
-        return paymentProcessor.createAuthorization(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
-                                                    SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
+        final Payment payment = paymentProcessor.createAuthorization(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
+                                                                     SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
+
+        final PaymentTransaction paymentTransaction = payment.getTransactions().get(payment.getTransactions().size() - 1);
+        logAPICall(transactionType,
+                   account,
+                   payment.getPaymentMethodId(),
+                   payment.getId(),
+                   paymentTransaction.getId(),
+                   paymentTransaction.getProcessedAmount(),
+                   paymentTransaction.getProcessedCurrency(),
+                   payment.getExternalKey(),
+                   paymentTransaction.getExternalKey(),
+                   paymentTransaction.getTransactionStatus(),
+                   null);
+
+        return payment;
     }
 
     @Override
@@ -101,11 +120,27 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
         }
         checkNotNullParameter(properties, "plugin properties");
 
-        logAPICall(TransactionType.AUTHORIZE.name(), account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey);
+        final String transactionType = TransactionType.AUTHORIZE.name();
+        logAPICall(transactionType, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, null, paymentControlPluginNames);
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
-        return pluginControlPaymentProcessor.createAuthorization(IS_API_PAYMENT, account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
-                                                                 properties, paymentControlPluginNames, callContext, internalCallContext);
+        final Payment payment = pluginControlPaymentProcessor.createAuthorization(IS_API_PAYMENT, account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
+                                                                                  properties, paymentControlPluginNames, callContext, internalCallContext);
+
+        final PaymentTransaction paymentTransaction = payment.getTransactions().get(payment.getTransactions().size() - 1);
+        logAPICall(transactionType,
+                   account,
+                   payment.getPaymentMethodId(),
+                   payment.getId(),
+                   paymentTransaction.getId(),
+                   paymentTransaction.getProcessedAmount(),
+                   paymentTransaction.getProcessedCurrency(),
+                   payment.getExternalKey(),
+                   paymentTransaction.getExternalKey(),
+                   paymentTransaction.getTransactionStatus(),
+                   paymentControlPluginNames);
+
+        return payment;
     }
 
     @Override
@@ -118,11 +153,27 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
         checkNotNullParameter(properties, "plugin properties");
         checkPositiveAmount(amount);
 
-        logAPICall(TransactionType.CAPTURE.name(), account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey);
+        final String transactionType = TransactionType.CAPTURE.name();
+        logAPICall(transactionType, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, null);
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
-        return paymentProcessor.createCapture(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, amount, currency, paymentTransactionExternalKey,
-                                              SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
+        final Payment payment = paymentProcessor.createCapture(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, amount, currency, paymentTransactionExternalKey,
+                                                               SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
+
+        final PaymentTransaction paymentTransaction = payment.getTransactions().get(payment.getTransactions().size() - 1);
+        logAPICall(transactionType,
+                   account,
+                   payment.getPaymentMethodId(),
+                   payment.getId(),
+                   paymentTransaction.getId(),
+                   paymentTransaction.getProcessedAmount(),
+                   paymentTransaction.getProcessedCurrency(),
+                   payment.getExternalKey(),
+                   paymentTransaction.getExternalKey(),
+                   paymentTransaction.getTransactionStatus(),
+                   null);
+
+        return payment;
     }
 
     @Override
@@ -139,9 +190,27 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
         checkNotNullParameter(properties, "plugin properties");
         checkPositiveAmount(amount);
 
+        final String transactionType = TransactionType.CAPTURE.name();
+        logAPICall(transactionType, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, paymentControlPluginNames);
+
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
-        return pluginControlPaymentProcessor.createCapture(IS_API_PAYMENT, account, paymentId, amount, currency, paymentTransactionExternalKey,
-                                                           properties, paymentControlPluginNames, callContext, internalCallContext);
+        final Payment payment = pluginControlPaymentProcessor.createCapture(IS_API_PAYMENT, account, paymentId, amount, currency, paymentTransactionExternalKey,
+                                                                            properties, paymentControlPluginNames, callContext, internalCallContext);
+
+        final PaymentTransaction paymentTransaction = payment.getTransactions().get(payment.getTransactions().size() - 1);
+        logAPICall(transactionType,
+                   account,
+                   payment.getPaymentMethodId(),
+                   payment.getId(),
+                   paymentTransaction.getId(),
+                   paymentTransaction.getProcessedAmount(),
+                   paymentTransaction.getProcessedCurrency(),
+                   payment.getExternalKey(),
+                   paymentTransaction.getExternalKey(),
+                   paymentTransaction.getTransactionStatus(),
+                   paymentControlPluginNames);
+
+        return payment;
     }
 
     @Override
@@ -156,15 +225,31 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
         }
         checkNotNullParameter(properties, "plugin properties");
 
-        logAPICall(TransactionType.PURCHASE.name(), account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey);
+        final String transactionType = TransactionType.PURCHASE.name();
+        logAPICall(transactionType, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, null, null);
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
-        return paymentProcessor.createPurchase(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
-                                               SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
+        final Payment payment = paymentProcessor.createPurchase(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
+                                                                SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
+
+        final PaymentTransaction paymentTransaction = payment.getTransactions().get(payment.getTransactions().size() - 1);
+        logAPICall(transactionType,
+                   account,
+                   payment.getPaymentMethodId(),
+                   payment.getId(),
+                   paymentTransaction.getId(),
+                   paymentTransaction.getProcessedAmount(),
+                   paymentTransaction.getProcessedCurrency(),
+                   payment.getExternalKey(),
+                   paymentTransaction.getExternalKey(),
+                   paymentTransaction.getTransactionStatus(),
+                   null);
+
+        return payment;
     }
 
     @Override
-    public Payment createPurchaseWithPaymentControl(final Account account, @Nullable final UUID paymentMethodId, @Nullable final UUID paymentId, final BigDecimal amount, final Currency currency, @Nullable  final String paymentExternalKey, final String paymentTransactionExternalKey,
+    public Payment createPurchaseWithPaymentControl(final Account account, @Nullable final UUID paymentMethodId, @Nullable final UUID paymentId, final BigDecimal amount, final Currency currency, @Nullable final String paymentExternalKey, final String paymentTransactionExternalKey,
                                                     final Iterable<PluginProperty> properties, final PaymentOptions paymentOptions, final CallContext callContext) throws PaymentApiException {
         final List<String> paymentControlPluginNames = toPaymentControlPluginNames(paymentOptions);
         if (paymentControlPluginNames.isEmpty()) {
@@ -180,20 +265,34 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
         checkNotNullParameter(paymentTransactionExternalKey, "paymentTransactionExternalKey");
         checkNotNullParameter(properties, "plugin properties");
 
-        logAPICall(TransactionType.PURCHASE.name(), account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey);
-
         if (paymentMethodId == null && !paymentOptions.isExternalPayment()) {
             throw new PaymentApiException(ErrorCode.PAYMENT_INVALID_PARAMETER, "paymentMethodId", "should not be null");
         }
-
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
-
         final UUID nonNulPaymentMethodId = (paymentMethodId != null) ?
                                            paymentMethodId :
                                            paymentMethodProcessor.createOrGetExternalPaymentMethod(UUIDs.randomUUID().toString(), account, properties, callContext, internalCallContext);
-        return pluginControlPaymentProcessor.createPurchase(IS_API_PAYMENT, account, nonNulPaymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
-                                                            properties, paymentControlPluginNames, callContext, internalCallContext);
 
+        final String transactionType = TransactionType.PURCHASE.name();
+        logAPICall(transactionType, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, null, paymentControlPluginNames);
+
+        final Payment payment = pluginControlPaymentProcessor.createPurchase(IS_API_PAYMENT, account, nonNulPaymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
+                                                                             properties, paymentControlPluginNames, callContext, internalCallContext);
+
+        final PaymentTransaction paymentTransaction = payment.getTransactions().get(payment.getTransactions().size() - 1);
+        logAPICall(transactionType,
+                   account,
+                   payment.getPaymentMethodId(),
+                   payment.getId(),
+                   paymentTransaction.getId(),
+                   paymentTransaction.getProcessedAmount(),
+                   paymentTransaction.getProcessedCurrency(),
+                   payment.getExternalKey(),
+                   paymentTransaction.getExternalKey(),
+                   paymentTransaction.getTransactionStatus(),
+                   paymentControlPluginNames);
+
+        return payment;
     }
 
     @Override
@@ -204,11 +303,27 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
         checkNotNullParameter(paymentId, "paymentId");
         checkNotNullParameter(properties, "plugin properties");
 
-        logAPICall(TransactionType.VOID.name(), account, null, paymentId, null, null, null, null, paymentTransactionExternalKey);
+        final String transactionType = TransactionType.VOID.name();
+        logAPICall(transactionType, account, null, paymentId, null, null, null, null, paymentTransactionExternalKey, null, null);
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
-        return paymentProcessor.createVoid(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, paymentTransactionExternalKey,
-                                           SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
+        final Payment payment = paymentProcessor.createVoid(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, paymentTransactionExternalKey,
+                                                            SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
+
+        final PaymentTransaction paymentTransaction = payment.getTransactions().get(payment.getTransactions().size() - 1);
+        logAPICall(transactionType,
+                   account,
+                   payment.getPaymentMethodId(),
+                   payment.getId(),
+                   paymentTransaction.getId(),
+                   paymentTransaction.getProcessedAmount(),
+                   paymentTransaction.getProcessedCurrency(),
+                   payment.getExternalKey(),
+                   paymentTransaction.getExternalKey(),
+                   paymentTransaction.getTransactionStatus(),
+                   null);
+
+        return payment;
 
     }
 
@@ -223,11 +338,27 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
         checkNotNullParameter(paymentId, "paymentId");
         checkNotNullParameter(properties, "plugin properties");
 
-        logAPICall(TransactionType.VOID.name(), account, null, paymentId, null, null, null, null, paymentTransactionExternalKey);
+        final String transactionType = TransactionType.VOID.name();
+        logAPICall(transactionType, account, null, paymentId, null, null, null, null, paymentTransactionExternalKey, null, paymentControlPluginNames);
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
-        return pluginControlPaymentProcessor.createVoid(IS_API_PAYMENT, account, paymentId, paymentTransactionExternalKey,
-                                                        properties, paymentControlPluginNames, callContext, internalCallContext);
+        final Payment payment = pluginControlPaymentProcessor.createVoid(IS_API_PAYMENT, account, paymentId, paymentTransactionExternalKey,
+                                                                         properties, paymentControlPluginNames, callContext, internalCallContext);
+
+        final PaymentTransaction paymentTransaction = payment.getTransactions().get(payment.getTransactions().size() - 1);
+        logAPICall(transactionType,
+                   account,
+                   payment.getPaymentMethodId(),
+                   payment.getId(),
+                   paymentTransaction.getId(),
+                   paymentTransaction.getProcessedAmount(),
+                   paymentTransaction.getProcessedCurrency(),
+                   payment.getExternalKey(),
+                   paymentTransaction.getExternalKey(),
+                   paymentTransaction.getTransactionStatus(),
+                   paymentControlPluginNames);
+
+        return payment;
     }
 
     @Override
@@ -243,11 +374,27 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
             checkPositiveAmount(amount);
         }
 
-        logAPICall(TransactionType.REFUND.name(), account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey);
+        final String transactionType = TransactionType.REFUND.name();
+        logAPICall(transactionType, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, null);
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
-        return paymentProcessor.createRefund(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, amount, currency, paymentTransactionExternalKey,
-                                             SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
+        final Payment payment = paymentProcessor.createRefund(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, amount, currency, paymentTransactionExternalKey,
+                                                              SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
+
+        final PaymentTransaction paymentTransaction = payment.getTransactions().get(payment.getTransactions().size() - 1);
+        logAPICall(transactionType,
+                   account,
+                   payment.getPaymentMethodId(),
+                   payment.getId(),
+                   paymentTransaction.getId(),
+                   paymentTransaction.getProcessedAmount(),
+                   paymentTransaction.getProcessedCurrency(),
+                   payment.getExternalKey(),
+                   paymentTransaction.getExternalKey(),
+                   paymentTransaction.getTransactionStatus(),
+                   null);
+
+        return payment;
     }
 
     @Override
@@ -269,12 +416,27 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
             checkPositiveAmount(amount);
         }
 
-        logAPICall(TransactionType.REFUND.name(), account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey);
+        final String transactionType = TransactionType.REFUND.name();
+        logAPICall(transactionType, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, paymentControlPluginNames);
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
-        return pluginControlPaymentProcessor.createRefund(IS_API_PAYMENT, account, paymentId, amount, currency, paymentTransactionExternalKey,
-                                                          properties, paymentControlPluginNames, callContext, internalCallContext);
+        final Payment payment = pluginControlPaymentProcessor.createRefund(IS_API_PAYMENT, account, paymentId, amount, currency, paymentTransactionExternalKey,
+                                                                           properties, paymentControlPluginNames, callContext, internalCallContext);
+
+        final PaymentTransaction paymentTransaction = payment.getTransactions().get(payment.getTransactions().size() - 1);
+        logAPICall(transactionType,
+                   account,
+                   payment.getPaymentMethodId(),
+                   payment.getId(),
+                   paymentTransaction.getId(),
+                   paymentTransaction.getProcessedAmount(),
+                   paymentTransaction.getProcessedCurrency(),
+                   payment.getExternalKey(),
+                   paymentTransaction.getExternalKey(),
+                   paymentTransaction.getTransactionStatus(),
+                   paymentControlPluginNames);
 
+        return payment;
     }
 
     @Override
@@ -290,12 +452,27 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
         }
         checkNotNullParameter(properties, "plugin properties");
 
-        logAPICall(TransactionType.CREDIT.name(), account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey);
+        final String transactionType = TransactionType.CREDIT.name();
+        logAPICall(transactionType, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, null, null);
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
-        return paymentProcessor.createCredit(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
-                                             SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
+        final Payment payment = paymentProcessor.createCredit(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
+                                                              SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
+
+        final PaymentTransaction paymentTransaction = payment.getTransactions().get(payment.getTransactions().size() - 1);
+        logAPICall(transactionType,
+                   account,
+                   payment.getPaymentMethodId(),
+                   payment.getId(),
+                   paymentTransaction.getId(),
+                   paymentTransaction.getProcessedAmount(),
+                   paymentTransaction.getProcessedCurrency(),
+                   payment.getExternalKey(),
+                   paymentTransaction.getExternalKey(),
+                   paymentTransaction.getTransactionStatus(),
+                   null);
 
+        return payment;
     }
 
     @Override
@@ -316,11 +493,27 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
         }
         checkNotNullParameter(properties, "plugin properties");
 
-        logAPICall(TransactionType.CREDIT.name(), account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey);
+        final String transactionType = TransactionType.CREDIT.name();
+        logAPICall(transactionType, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, null, paymentControlPluginNames);
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
-        return pluginControlPaymentProcessor.createCredit(IS_API_PAYMENT, account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
-                                                          properties, paymentControlPluginNames, callContext, internalCallContext);
+        final Payment payment = pluginControlPaymentProcessor.createCredit(IS_API_PAYMENT, account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
+                                                                           properties, paymentControlPluginNames, callContext, internalCallContext);
+
+        final PaymentTransaction paymentTransaction = payment.getTransactions().get(payment.getTransactions().size() - 1);
+        logAPICall(transactionType,
+                   account,
+                   payment.getPaymentMethodId(),
+                   payment.getId(),
+                   paymentTransaction.getId(),
+                   paymentTransaction.getProcessedAmount(),
+                   paymentTransaction.getProcessedCurrency(),
+                   payment.getExternalKey(),
+                   paymentTransaction.getExternalKey(),
+                   paymentTransaction.getTransactionStatus(),
+                   paymentControlPluginNames);
+
+        return payment;
     }
 
     @Override
@@ -328,10 +521,32 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
         checkNotNullParameter(account, "account");
         checkNotNullParameter(paymentTransactionId, "paymentTransactionId");
 
-        logAPICall("NOTIFY_STATE_CHANGE", account, null, null, paymentTransactionId, null, null, null, null);
+        final String transactionType = "NOTIFY_STATE_CHANGE";
+        logAPICall(transactionType, account, null, null, paymentTransactionId, null, null, null, null, null, null);
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
-        return paymentProcessor.notifyPendingPaymentOfStateChanged(account, paymentTransactionId, isSuccess, callContext, internalCallContext);
+        final Payment payment = paymentProcessor.notifyPendingPaymentOfStateChanged(account, paymentTransactionId, isSuccess, callContext, internalCallContext);
+
+        final PaymentTransaction paymentTransaction = Iterables.<PaymentTransaction>tryFind(payment.getTransactions(),
+                                                                                            new Predicate<PaymentTransaction>() {
+                                                                                                @Override
+                                                                                                public boolean apply(final PaymentTransaction transaction) {
+                                                                                                    return transaction.getId().equals(paymentTransactionId);
+                                                                                                }
+                                                                                            }).orNull();
+        logAPICall(transactionType,
+                   account,
+                   payment.getPaymentMethodId(),
+                   payment.getId(),
+                   paymentTransaction == null ? null : paymentTransaction.getId(),
+                   paymentTransaction == null ? null : paymentTransaction.getProcessedAmount(),
+                   paymentTransaction == null ? null : paymentTransaction.getProcessedCurrency(),
+                   payment.getExternalKey(),
+                   paymentTransaction == null ? null : paymentTransaction.getExternalKey(),
+                   paymentTransaction == null ? null : paymentTransaction.getTransactionStatus(),
+                   null);
+
+        return payment;
     }
 
     @Override
@@ -347,12 +562,27 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
         checkNotNullParameter(paymentId, "paymentId");
         checkPositiveAmount(amount);
 
-        logAPICall(TransactionType.CHARGEBACK.name(), account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey);
+        final String transactionType = TransactionType.CHARGEBACK.name();
+        logAPICall(transactionType, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, null);
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
-        return paymentProcessor.createChargeback(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, paymentTransactionExternalKey, amount, currency, true,
-                                                 callContext, internalCallContext);
+        final Payment payment = paymentProcessor.createChargeback(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, paymentTransactionExternalKey, amount, currency, true,
+                                                                  callContext, internalCallContext);
+
+        final PaymentTransaction paymentTransaction = payment.getTransactions().get(payment.getTransactions().size() - 1);
+        logAPICall(transactionType,
+                   account,
+                   payment.getPaymentMethodId(),
+                   payment.getId(),
+                   paymentTransaction.getId(),
+                   paymentTransaction.getProcessedAmount(),
+                   paymentTransaction.getProcessedCurrency(),
+                   payment.getExternalKey(),
+                   paymentTransaction.getExternalKey(),
+                   paymentTransaction.getTransactionStatus(),
+                   null);
 
+        return payment;
     }
 
     @Override
@@ -368,9 +598,27 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
         checkNotNullParameter(paymentId, "paymentId");
         checkPositiveAmount(amount);
 
+        final String transactionType = TransactionType.CHARGEBACK.name();
+        logAPICall(transactionType, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, paymentControlPluginNames);
+
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
-        return pluginControlPaymentProcessor.createChargeback(IS_API_PAYMENT, account, paymentId, paymentTransactionExternalKey, amount, currency,
-                                                              paymentControlPluginNames, callContext, internalCallContext);
+        final Payment payment = pluginControlPaymentProcessor.createChargeback(IS_API_PAYMENT, account, paymentId, paymentTransactionExternalKey, amount, currency,
+                                                                               paymentControlPluginNames, callContext, internalCallContext);
+
+        final PaymentTransaction paymentTransaction = payment.getTransactions().get(payment.getTransactions().size() - 1);
+        logAPICall(transactionType,
+                   account,
+                   payment.getPaymentMethodId(),
+                   payment.getId(),
+                   paymentTransaction.getId(),
+                   paymentTransaction.getProcessedAmount(),
+                   paymentTransaction.getProcessedCurrency(),
+                   payment.getExternalKey(),
+                   paymentTransaction.getExternalKey(),
+                   paymentTransaction.getTransactionStatus(),
+                   paymentControlPluginNames);
+
+        return payment;
     }
 
     @Override
diff --git a/payment/src/main/java/org/killbill/billing/payment/bus/PaymentBusEventHandler.java b/payment/src/main/java/org/killbill/billing/payment/bus/PaymentBusEventHandler.java
index c7705ec..45f8ba0 100644
--- a/payment/src/main/java/org/killbill/billing/payment/bus/PaymentBusEventHandler.java
+++ b/payment/src/main/java/org/killbill/billing/payment/bus/PaymentBusEventHandler.java
@@ -80,8 +80,7 @@ public class PaymentBusEventHandler {
     @AllowConcurrentEvents
     @Subscribe
     public void processInvoiceEvent(final InvoiceCreationInternalEvent event) {
-        log.info("Received invoice creation notification for account {} and invoice {}",
-                 event.getAccountId(), event.getInvoiceId());
+        log.info("Received invoice creation notification for accountId='{}', invoiceId='{}'", event.getAccountId(), event.getInvoiceId());
 
         final Account account;
         try {
@@ -100,7 +99,7 @@ public class PaymentBusEventHandler {
             pluginControlPaymentProcessor.createPurchase(false, account, account.getPaymentMethodId(), null, amountToBePaid, account.getCurrency(), UUIDs.randomUUID().toString(), UUIDs.randomUUID().toString(),
                                                          properties, paymentControlPluginNames, callContext, internalContext);
         } catch (final AccountApiException e) {
-            log.error("Failed to process invoice payment", e);
+            log.warn("Failed to process invoice payment", e);
         } catch (final PaymentApiException e) {
             // Log as error unless:
             if (e.getCode() != ErrorCode.PAYMENT_NULL_INVOICE.getCode() /* Nothing left to be paid */ &&
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/janitor/CompletionTaskBase.java b/payment/src/main/java/org/killbill/billing/payment/core/janitor/CompletionTaskBase.java
index 4803871..6a86654 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/janitor/CompletionTaskBase.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/janitor/CompletionTaskBase.java
@@ -125,9 +125,9 @@ abstract class CompletionTaskBase<T> implements Runnable {
             lock = locker.lockWithNumberOfTries(LockerType.ACCNT_INV_PAY.toString(), account.getExternalKey(), paymentConfig.getMaxGlobalLockRetries());
             return callback.doIteration();
         } catch (AccountApiException e) {
-            log.warn(String.format("Janitor failed to retrieve account with recordId %s", internalTenantContext.getAccountRecordId()), e);
+            log.warn("Error retrieving accountRecordId='{}'", internalTenantContext.getAccountRecordId(), e);
         } catch (LockFailedException e) {
-            log.warn(String.format("Janitor failed to lock account with recordId %s", internalTenantContext.getAccountRecordId()), e);
+            log.warn("Error locking accountRecordId='{}'", internalTenantContext.getAccountRecordId(), e);
         } finally {
             if (lock != null) {
                 lock.release();
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentAttemptTask.java b/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentAttemptTask.java
index 9851afb..ee44e8b 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentAttemptTask.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentAttemptTask.java
@@ -124,7 +124,7 @@ public class IncompletePaymentAttemptTask extends CompletionTaskBase<PaymentAtte
         if (transaction == null ||
             transaction.getTransactionStatus() == TransactionStatus.PLUGIN_FAILURE ||
             transaction.getTransactionStatus() == TransactionStatus.PAYMENT_FAILURE) {
-            log.info("Janitor AttemptCompletionTask moving attempt " + attempt.getId() + " -> ABORTED");
+            log.info("Moving attemptId='{}' to ABORTED", attempt.getId());
             paymentDao.updatePaymentAttempt(attempt.getId(), attempt.getTransactionId(), "ABORTED", internalCallContext);
             return;
         }
@@ -140,7 +140,7 @@ public class IncompletePaymentAttemptTask extends CompletionTaskBase<PaymentAtte
             transaction.getTransactionStatus() == TransactionStatus.PENDING) {
 
             try {
-                log.info("Janitor AttemptCompletionTask completing attempt " + attempt.getId() + " -> SUCCESS");
+                log.info("Moving attemptId='{}' to SUCCESS", attempt.getId());
 
                 final Account account = accountInternalApi.getAccountById(attempt.getAccountId(), tenantContext);
                 final boolean isApiPayment = true; // unclear
@@ -166,11 +166,11 @@ public class IncompletePaymentAttemptTask extends CompletionTaskBase<PaymentAtte
                 //
                 pluginControlledPaymentAutomatonRunner.completeRun(paymentStateContext);
             } catch (final AccountApiException e) {
-                log.warn("Janitor AttemptCompletionTask failed to complete payment attempt " + attempt.getId(), e);
+                log.warn("Error completing paymentAttemptId='{}'", attempt.getId(), e);
             } catch (final PluginPropertySerializerException e) {
-                log.warn("Janitor AttemptCompletionTask failed to complete payment attempt " + attempt.getId(), e);
+                log.warn("Error completing paymentAttemptId='{}'", attempt.getId(), e);
             } catch (final PaymentApiException e) {
-                log.warn("Janitor AttemptCompletionTask failed to complete payment attempt " + attempt.getId(), e);
+                log.warn("Error completing paymentAttemptId='{}'", attempt.getId(), e);
             }
         }
     }
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java b/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java
index c71407d..3be1735 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java
@@ -192,7 +192,7 @@ public class IncompletePaymentTransactionTask extends CompletionTaskBase<Payment
             case UNKNOWN:
             default:
                 if (transactionStatus != paymentTransaction.getTransactionStatus()) {
-                    log.info("Janitor IncompletePaymentTransactionTask unable to repair payment {}, transaction {}: {} -> {}",
+                    log.info("Unable to repair paymentId='{}', paymentTransactionId='{}', currentTransactionStatus='{}', newTransactionStatus='{}'",
                              payment.getId(), paymentTransaction.getId(), paymentTransaction.getTransactionStatus(), transactionStatus);
                 }
                 // We can't get anything interesting from the plugin...
@@ -233,8 +233,7 @@ public class IncompletePaymentTransactionTask extends CompletionTaskBase<Payment
         final String gatewayErrorCode = paymentTransactionInfoPlugin != null ? paymentTransactionInfoPlugin.getGatewayErrorCode() : paymentTransaction.getGatewayErrorCode();
         final String gatewayError = paymentTransactionInfoPlugin != null ? paymentTransactionInfoPlugin.getGatewayError() : paymentTransaction.getGatewayErrorMsg();
 
-
-        log.info("Janitor IncompletePaymentTransactionTask repairing payment {}, transaction {}, transitioning transactionStatus from {} -> {}",
+        log.info("Repairing paymentId='{}', paymentTransactionId='{}', currentTransactionStatus='{}', newTransactionStatus='{}'",
                  payment.getId(), paymentTransaction.getId(), paymentTransaction.getTransactionStatus(), transactionStatus);
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(payment.getAccountId(), callContext);
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/janitor/Janitor.java b/payment/src/main/java/org/killbill/billing/payment/core/janitor/Janitor.java
index fbac8e0..c17f17c 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/janitor/Janitor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/janitor/Janitor.java
@@ -135,7 +135,7 @@ public class Janitor {
                                                                             @Override
                                                                             public void handleReadyNotification(final NotificationEvent notificationKey, final DateTime eventDateTime, final UUID userToken, final Long accountRecordId, final Long tenantRecordId) {
                                                                                 if (!(notificationKey instanceof JanitorNotificationKey)) {
-                                                                                    log.error("Janitor service received an unexpected event type {}" + notificationKey.getClass().getName());
+                                                                                    log.error("Janitor service received an unexpected event className='{}", notificationKey.getClass());
                                                                                     return;
 
                                                                                 }
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PaymentExecutors.java b/payment/src/main/java/org/killbill/billing/payment/core/PaymentExecutors.java
index 18fcb1e..d58c304 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PaymentExecutors.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PaymentExecutors.java
@@ -21,6 +21,7 @@ import java.util.concurrent.ExecutorService;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 
 import javax.inject.Inject;
@@ -43,7 +44,7 @@ public class PaymentExecutors {
 
     private final PaymentConfig paymentConfig;
 
-    private volatile ExecutorService pluginExecutorService;
+    private volatile ThreadPoolExecutor pluginExecutorService;
     private volatile ScheduledExecutorService janitorExecutorService;
 
     @Inject
@@ -54,6 +55,7 @@ public class PaymentExecutors {
 
     public void initialize() {
         this.pluginExecutorService = createPluginExecutorService();
+        this.pluginExecutorService.prestartAllCoreThreads();
         this.janitorExecutorService = createJanitorExecutorService();
     }
 
@@ -77,7 +79,7 @@ public class PaymentExecutors {
         return janitorExecutorService;
     }
 
-    private ExecutorService createPluginExecutorService() {
+    private ThreadPoolExecutor createPluginExecutorService() {
         final int minThreadNb = DEFAULT_MIN_PLUGIN_THREADS < paymentConfig.getPaymentPluginThreadNb() ? DEFAULT_MIN_PLUGIN_THREADS : paymentConfig.getPaymentPluginThreadNb();
         return new WithProfilingThreadPoolExecutor(minThreadNb,
                                                    paymentConfig.getPaymentPluginThreadNb(),
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PaymentMethodProcessor.java b/payment/src/main/java/org/killbill/billing/payment/core/PaymentMethodProcessor.java
index 4ab1a19..e8324fb 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PaymentMethodProcessor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PaymentMethodProcessor.java
@@ -139,7 +139,6 @@ public class PaymentMethodProcessor extends ProcessorBase {
                                                                                                                     accountInternalApi.updatePaymentMethod(account.getId(), pm.getId(), context);
                                                                                                                 }
                                                                                                             } catch (final PaymentPluginApiException e) {
-                                                                                                                log.warn("Error adding payment method " + pm.getId() + " for plugin " + paymentPluginServiceName, e);
                                                                                                                 throw new PaymentApiException(ErrorCode.PAYMENT_ADD_PAYMENT_METHOD, account.getId(), e.getErrorMessage());
                                                                                                             } catch (final AccountApiException e) {
                                                                                                                 throw new PaymentApiException(e);
@@ -176,7 +175,11 @@ public class PaymentMethodProcessor extends ProcessorBase {
         try {
             paymentMethodPlugin = pluginApi.getPaymentMethodDetail(account.getId(), pm.getId(), properties, callContext);
         } catch (final PaymentPluginApiException e) {
-            log.warn("Error retrieving payment method " + pm.getId() + " from plugin " + pm.getPluginName(), e);
+            if (e.getCause() == null) {
+                log.warn("Error retrieving paymentMethodId='{}', plugin='{}', errorMessage='{}', errorType='{}'", pm.getId(), pm.getPluginName(), e.getErrorMessage(), e.getErrorType());
+            } else {
+                log.warn("Error retrieving paymentMethodId='{}', plugin='{}', errorMessage='{}', errorType='{}'", pm.getId(), pm.getPluginName(), e.getErrorMessage(), e.getErrorType(), e);
+            }
             return null;
         }
 
@@ -227,7 +230,6 @@ public class PaymentMethodProcessor extends ProcessorBase {
                 final PaymentPluginApi pluginApi = getPaymentPluginApi(paymentMethodModelDao.getPluginName());
                 paymentMethodPlugin = pluginApi.getPaymentMethodDetail(paymentMethodModelDao.getAccountId(), paymentMethodModelDao.getId(), properties, tenantContext);
             } catch (final PaymentPluginApiException e) {
-                log.warn("Error retrieving payment method " + paymentMethodModelDao.getId() + " from plugin " + paymentMethodModelDao.getPluginName(), e);
                 throw new PaymentApiException(ErrorCode.PAYMENT_GET_PAYMENT_METHODS, paymentMethodModelDao.getAccountId(), paymentMethodModelDao.getId());
             }
         } else {
@@ -270,7 +272,11 @@ public class PaymentMethodProcessor extends ProcessorBase {
                                                try {
                                                    paymentMethodPlugin = pluginApi.getPaymentMethodDetail(paymentMethodModelDao.getAccountId(), paymentMethodModelDao.getId(), properties, tenantContext);
                                                } catch (final PaymentPluginApiException e) {
-                                                   log.warn("Unable to find payment method id " + paymentMethodModelDao.getId() + " in plugin " + pluginName);
+                                                   if (e.getCause() == null) {
+                                                       log.warn("Error retrieving paymentMethodId='{}', plugin='{}', errorMessage='{}', errorType='{}'", paymentMethodModelDao.getId(), pluginName, e.getErrorMessage(), e.getErrorType());
+                                                   } else {
+                                                       log.warn("Error retrieving paymentMethodId='{}', plugin='{}', errorMessage='{}', errorType='{}'", paymentMethodModelDao.getId(), pluginName, e.getErrorMessage(), e.getErrorType(), e);
+                                                   }
                                                    // We still want to return a payment method object, even though the plugin details are missing
                                                }
                                            }
@@ -413,7 +419,7 @@ public class PaymentMethodProcessor extends ProcessorBase {
                             } else {
                                 final boolean isAccountAutoPayOff = isAccountAutoPayOff(account.getId(), context);
                                 if (!isAccountAutoPayOff) {
-                                    log.info("Setting account {} to AUTO_PAY_OFF because of default payment method deletion", account.getId());
+                                    log.info("Setting AUTO_PAY_OFF on accountId='{}' because of default payment method deletion", account.getId());
                                     setAccountAutoPayOff(account.getId(), context);
                                 }
                                 accountInternalApi.removePaymentMethod(account.getId(), context);
@@ -424,7 +430,6 @@ public class PaymentMethodProcessor extends ProcessorBase {
                         paymentDao.deletedPaymentMethod(paymentMethodId, context);
                         return PluginDispatcher.createPluginDispatcherReturnType(null);
                     } catch (final PaymentPluginApiException e) {
-                        log.warn("Error deleting payment method " + paymentMethodId, e);
                         throw new PaymentApiException(ErrorCode.PAYMENT_DEL_PAYMENT_METHOD, account.getId(), e.getErrorMessage());
                     } catch (final AccountApiException e) {
                         throw new PaymentApiException(e);
@@ -502,7 +507,6 @@ public class PaymentMethodProcessor extends ProcessorBase {
                 return ImmutableList.<PaymentMethod>of();
             }
         } catch (final PaymentPluginApiException e) {
-            log.warn("Error refreshing payment methods for account " + account.getId() + " and plugin " + pluginName, e);
             throw new PaymentApiException(ErrorCode.PAYMENT_REFRESH_PAYMENT_METHOD, account.getId(), e.getErrorMessage());
         }
 
@@ -541,7 +545,6 @@ public class PaymentMethodProcessor extends ProcessorBase {
                     try {
                         pluginApi.resetPaymentMethods(account.getId(), pluginPmsWithId, properties, callContext);
                     } catch (final PaymentPluginApiException e) {
-                        log.warn("Error resetting payment methods for account " + account.getId() + " and plugin " + pluginName, e);
                         throw new PaymentApiException(ErrorCode.PAYMENT_REFRESH_PAYMENT_METHOD, account.getId(), e.getErrorMessage());
                     }
                     try {
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PluginControlPaymentProcessor.java b/payment/src/main/java/org/killbill/billing/payment/core/PluginControlPaymentProcessor.java
index 85ba858..17e87d8 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PluginControlPaymentProcessor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PluginControlPaymentProcessor.java
@@ -51,12 +51,16 @@ import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.clock.Clock;
 import org.killbill.commons.locker.GlobalLocker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableList;
 
 public class PluginControlPaymentProcessor extends ProcessorBase {
 
+    private static final Logger log = LoggerFactory.getLogger(PluginControlPaymentProcessor.class);
+
     private static final Joiner JOINER = Joiner.on(", ");
 
     private final PluginControlPaymentAutomatonRunner pluginControlledPaymentAutomatonRunner;
@@ -225,20 +229,20 @@ public class PluginControlPaymentProcessor extends ProcessorBase {
                                                        internalCallContext);
 
         } catch (final AccountApiException e) {
-            log.warn("Failed to retry attempt " + attemptId + toPluginNamesOnError(" for plugins ", paymentControlPluginNames), e);
+            log.warn("Failed to retry attemptId='{}', paymentControlPlugins='{}'", attemptId, toPluginNamesOnError(paymentControlPluginNames), e);
         } catch (final PaymentApiException e) {
-            log.warn("Failed to retry attempt " + attemptId + toPluginNamesOnError(" for plugins ", paymentControlPluginNames), e);
+            log.warn("Failed to retry attemptId='{}', paymentControlPlugins='{}'", attemptId, toPluginNamesOnError(paymentControlPluginNames), e);
         } catch (final PluginPropertySerializerException e) {
-            log.warn("Failed to retry attempt " + attemptId + toPluginNamesOnError(" for plugins ", paymentControlPluginNames), e);
+            log.warn("Failed to retry attemptId='{}', paymentControlPlugins='{}'", attemptId, toPluginNamesOnError(paymentControlPluginNames), e);
         } catch (final MissingEntryException e) {
-            log.warn("Failed to retry attempt " + attemptId + toPluginNamesOnError(" for plugins ", paymentControlPluginNames), e);
+            log.warn("Failed to retry attemptId='{}', paymentControlPlugins='{}'", attemptId, toPluginNamesOnError(paymentControlPluginNames), e);
         }
     }
 
-    private String toPluginNamesOnError(final String prefixMessage, final Collection<String> paymentControlPluginNames) {
+    private String toPluginNamesOnError(final Collection<String> paymentControlPluginNames) {
         if (paymentControlPluginNames == null || paymentControlPluginNames.isEmpty()) {
             return "";
         }
-        return prefixMessage + "(" + JOINER.join(paymentControlPluginNames) + ")";
+        return JOINER.join(paymentControlPluginNames);
     }
 }
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java b/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java
index dd41668..31ac463 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java
@@ -59,6 +59,8 @@ import com.google.common.collect.Collections2;
 
 public abstract class ProcessorBase {
 
+    private static final Logger log = LoggerFactory.getLogger(ProcessorBase.class);
+
     protected final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry;
     protected final AccountInternalApi accountInternalApi;
     protected final GlobalLocker locker;
@@ -66,8 +68,6 @@ public abstract class ProcessorBase {
     protected final InternalCallContextFactory internalCallContextFactory;
     protected final TagInternalApi tagInternalApi;
     protected final Clock clock;
-
-    protected static final Logger log = LoggerFactory.getLogger(ProcessorBase.class);
     protected final InvoiceInternalApi invoiceApi;
 
     public ProcessorBase(final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry,
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ControlPluginRunner.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ControlPluginRunner.java
index 7d4e20b..7f0ba27 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ControlPluginRunner.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ControlPluginRunner.java
@@ -187,9 +187,9 @@ public class ControlPluginRunner {
                     }
                     // Exceptions from the control plugins are ignored (and logged) because the semantics on what to do are undefined.
                 } catch (final PaymentControlApiException e) {
-                    log.warn("Plugin " + pluginName + " failed to complete executePluginOnSuccessCalls call for " + inputPaymentControlContext.getPaymentExternalKey(), e);
+                    log.warn("Error during onSuccessCall for plugin='{}', paymentExternalKey='{}'", pluginName, inputPaymentControlContext.getPaymentExternalKey(), e);
                 } catch (final RuntimeException e) {
-                    log.warn("Plugin " + pluginName + " failed to complete executePluginOnSuccessCalls call for " + inputPaymentControlContext.getPaymentExternalKey(), e);
+                    log.warn("Error during onSuccessCall for plugin='{}', paymentExternalKey='{}'", pluginName, inputPaymentControlContext.getPaymentExternalKey(), e);
                 }
             }
         }
@@ -247,7 +247,7 @@ public class ControlPluginRunner {
                     }
 
                 } catch (final PaymentControlApiException e) {
-                    log.warn("Plugin " + pluginName + " failed to return next retryDate for payment " + inputPaymentControlContext.getPaymentExternalKey(), e);
+                    log.warn("Error during onFailureCall for plugin='{}', paymentExternalKey='{}'", pluginName, inputPaymentControlContext.getPaymentExternalKey(), e);
                     return new DefaultFailureCallResult(candidate, inputPluginProperties);
                 }
             }
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/OperationControlCallback.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/OperationControlCallback.java
index a463954..22b0d54 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/OperationControlCallback.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/OperationControlCallback.java
@@ -150,31 +150,32 @@ public abstract class OperationControlCallback extends OperationCallbackBase<Pay
                 } catch (final RuntimeException e) {
                     // Attempts to set the retry date in context if needed.
                     executePluginOnFailureCallsAndSetRetryDate(paymentControlContext);
-                    throw e;
+                    throw new OperationException(e, OperationResult.EXCEPTION);
                 }
             }
         });
     }
 
     @Override
-    protected OperationException unwrapExceptionFromDispatchedTask(final PaymentStateContext paymentStateContext, final Exception e) {
-
+    protected OperationException unwrapExceptionFromDispatchedTask(final Exception e) {
         // If this is an ExecutionException we attempt to extract the cause first
-        final Throwable originalExceptionOrCause = e instanceof ExecutionException ? MoreObjects.firstNonNull(e.getCause(), e) : e;
+        final Throwable originalExceptionOrCausePossiblyOperationException = e instanceof ExecutionException ? MoreObjects.firstNonNull(e.getCause(), e) : e;
+
+        // Unwrap OperationException too (doOperationCallback wraps exceptions in OperationException)
+        final Throwable originalExceptionOrCause = originalExceptionOrCausePossiblyOperationException instanceof OperationException ? MoreObjects.firstNonNull(originalExceptionOrCausePossiblyOperationException.getCause(), originalExceptionOrCausePossiblyOperationException) : originalExceptionOrCausePossiblyOperationException;
 
         if (originalExceptionOrCause instanceof OperationException) {
             return (OperationException) originalExceptionOrCause;
         } else if (originalExceptionOrCause instanceof LockFailedException) {
-            final String format = String.format("Failed to lock account %s", paymentStateContext.getAccount().getExternalKey());
-            logger.error(String.format(format));
+            logger.warn("Failed to lock accountId='{}'", paymentStateContext.getAccount().getId());
         } else if (originalExceptionOrCause instanceof TimeoutException) {
-            logger.warn("RetryOperationCallback call TIMEOUT for account {}", paymentStateContext.getAccount().getExternalKey());
+            logger.warn("Call TIMEOUT for accountId='{}'", paymentStateContext.getAccount().getId());
         } else if (originalExceptionOrCause instanceof InterruptedException) {
-            logger.error("RetryOperationCallback call was interrupted for account {}", paymentStateContext.getAccount().getExternalKey());
-        } else /* most probably RuntimeException */ {
-            logger.warn("RetryOperationCallback failed for account {}", paymentStateContext.getAccount().getExternalKey(), e);
+            logger.warn("Call was interrupted for accountId='{}'", paymentStateContext.getAccount().getId());
+        } else {
+            logger.warn("Operation failed for accountId='{}'", paymentStateContext.getAccount().getId(), e);
         }
-        return new OperationException(e, getOperationResultOnException(paymentStateContext));
+        return new OperationException(originalExceptionOrCause, getOperationResultOnException(paymentStateContext));
     }
 
     private OperationResult getOperationResultOnException(final PaymentStateContext paymentStateContext) {
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/OperationCallbackBase.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/OperationCallbackBase.java
index f5c542d..1f52802 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/OperationCallbackBase.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/OperationCallbackBase.java
@@ -35,7 +35,7 @@ import org.slf4j.LoggerFactory;
 
 public abstract class OperationCallbackBase<CallbackOperationResult, CallbackOperationException extends Exception> {
 
-    protected final Logger logger = LoggerFactory.getLogger(OperationCallbackBase.class);
+    private final Logger logger = LoggerFactory.getLogger(OperationCallbackBase.class);
 
     private final GlobalLocker locker;
     private final PluginDispatcher<OperationResult> paymentPluginDispatcher;
@@ -72,14 +72,14 @@ public abstract class OperationCallbackBase<CallbackOperationResult, CallbackOpe
             logger.debug("Successful plugin(s) call of {} for account {} with result {}", pluginNames, account.getExternalKey(), operationResult);
             return operationResult;
         } catch (final ExecutionException e) {
-            throw unwrapExceptionFromDispatchedTask(paymentStateContext, e);
+            throw unwrapExceptionFromDispatchedTask(e);
         } catch (final TimeoutException e) {
             logger.warn("TimeoutException while executing the plugin(s) {}", pluginNames);
-            throw unwrapExceptionFromDispatchedTask(paymentStateContext, e);
+            throw unwrapExceptionFromDispatchedTask(e);
         } catch (final InterruptedException e) {
             Thread.currentThread().interrupt();
             logger.warn("InterruptedException while executing the following plugin(s): {}", pluginNames);
-            throw unwrapExceptionFromDispatchedTask(paymentStateContext, e);
+            throw unwrapExceptionFromDispatchedTask(e);
         }
     }
 
@@ -91,5 +91,5 @@ public abstract class OperationCallbackBase<CallbackOperationResult, CallbackOpe
     //
     protected abstract CallbackOperationResult doCallSpecificOperationCallback() throws CallbackOperationException;
 
-    protected abstract OperationException unwrapExceptionFromDispatchedTask(final PaymentStateContext paymentStateContext, final Exception e);
+    protected abstract OperationException unwrapExceptionFromDispatchedTask(final Exception e);
 }
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentEnteringStateCallback.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentEnteringStateCallback.java
index f5dd2f2..d336109 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentEnteringStateCallback.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentEnteringStateCallback.java
@@ -76,7 +76,7 @@ public abstract class PaymentEnteringStateCallback implements EnteringStateCallb
             try {
                 daoHelper.getEventBus().post(event);
             } catch (EventBusException e) {
-                logger.error("Failed to post Payment event event for account {} ", paymentStateContext.getAccount().getId(), e);
+                logger.warn("Failed to post event {}", event, e);
             }
         }
     }
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentOperation.java
index 4b11c6b..e269932 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentOperation.java
@@ -45,6 +45,8 @@ import org.killbill.billing.payment.provider.DefaultNoOpPaymentInfoPlugin;
 import org.killbill.billing.util.config.PaymentConfig;
 import org.killbill.commons.locker.GlobalLocker;
 import org.killbill.commons.locker.LockFailedException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Predicate;
@@ -54,6 +56,8 @@ import com.google.common.collect.Iterables;
 // Encapsulates the payment specific logic
 public abstract class PaymentOperation extends OperationCallbackBase<PaymentTransactionInfoPlugin, PaymentPluginApiException> implements OperationCallback {
 
+    private final Logger logger = LoggerFactory.getLogger(PaymentOperation.class);
+
     protected final PaymentAutomatonDAOHelper daoHelper;
     protected PaymentPluginApi plugin;
 
@@ -68,41 +72,49 @@ public abstract class PaymentOperation extends OperationCallbackBase<PaymentTran
 
     @Override
     public OperationResult doOperationCallback() throws OperationException {
+        final String pluginName;
         try {
-            final String pluginName = daoHelper.getPaymentProviderPluginName();
+            pluginName = daoHelper.getPaymentProviderPluginName();
             this.plugin = daoHelper.getPaymentPluginApi(pluginName);
+        } catch (final PaymentApiException e) {
+            throw convertToUnknownTransactionStatusAndErroredPaymentState(e);
+        }
 
-            if (paymentStateContext.shouldLockAccountAndDispatch()) {
-                return doOperationCallbackWithDispatchAndAccountLock(pluginName);
-            } else {
+        if (paymentStateContext.shouldLockAccountAndDispatch()) {
+            // This will already call unwrapExceptionFromDispatchedTask
+            return doOperationCallbackWithDispatchAndAccountLock(pluginName);
+        } else {
+            try {
                 return doSimpleOperationCallback();
+            } catch (final Exception e) {
+                // We need to unwrap OperationException (see doSimpleOperationCallback below)
+                throw unwrapExceptionFromDispatchedTask(e);
             }
-        } catch (final Exception e) {
-            throw convertToUnknownTransactionStatusAndErroredPaymentState(e);
         }
     }
 
     @Override
-    protected OperationException unwrapExceptionFromDispatchedTask(final PaymentStateContext paymentStateContext, final Exception e) {
-
+    protected OperationException unwrapExceptionFromDispatchedTask(final Exception e) {
         // If this is an ExecutionException we attempt to extract the cause first
-        final Throwable originalExceptionOrCause = e instanceof ExecutionException ? MoreObjects.firstNonNull(e.getCause(), e) : e;
+        final Throwable originalExceptionOrCausePossiblyOperationException = e instanceof ExecutionException ? MoreObjects.firstNonNull(e.getCause(), e) : e;
+
+        // Unwrap OperationException too (doOperationCallback wraps exceptions in OperationException)
+        final Throwable originalExceptionOrCause = originalExceptionOrCausePossiblyOperationException instanceof OperationException ? MoreObjects.firstNonNull(originalExceptionOrCausePossiblyOperationException.getCause(), originalExceptionOrCausePossiblyOperationException) : originalExceptionOrCausePossiblyOperationException;
 
         //
         // Any case of exception (checked or runtime) should lead to a TransactionStatus.UNKNOWN (and a XXX_ERRORED payment state).
         // In order to reach that state we create PaymentTransactionInfoPlugin with an PaymentPluginStatus.UNDEFINED status (and an OperationResult.EXCEPTION).
         //
         if (originalExceptionOrCause instanceof LockFailedException) {
-            logger.warn("Failed to lock account {}", paymentStateContext.getAccount().getExternalKey());
+            logger.warn("Failed to lock accountExternalKey='{}'", paymentStateContext.getAccount().getExternalKey());
         } else if (originalExceptionOrCause instanceof TimeoutException) {
-            logger.error("Plugin call TIMEOUT for account {}", paymentStateContext.getAccount().getExternalKey());
+            logger.warn("Plugin call TIMEOUT for accountExternalKey='{}'", paymentStateContext.getAccount().getExternalKey());
         } else if (originalExceptionOrCause instanceof InterruptedException) {
-            logger.error("Plugin call was interrupted for account {}", paymentStateContext.getAccount().getExternalKey());
+            logger.warn("Plugin call was interrupted for accountExternalKey='{}'", paymentStateContext.getAccount().getExternalKey());
         } else {
-            logger.warn("Payment plugin call threw an exception for account {}", paymentStateContext.getAccount().getExternalKey(), originalExceptionOrCause);
+            logger.warn("Payment plugin call threw an exception for accountExternalKey='{}'", paymentStateContext.getAccount().getExternalKey(), originalExceptionOrCause);
         }
         return convertToUnknownTransactionStatusAndErroredPaymentState(originalExceptionOrCause);
-
     }
 
     //
diff --git a/payment/src/main/java/org/killbill/billing/payment/dispatcher/CallableWithRequestData.java b/payment/src/main/java/org/killbill/billing/payment/dispatcher/CallableWithRequestData.java
index d8012d2..01b15f1 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dispatcher/CallableWithRequestData.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dispatcher/CallableWithRequestData.java
@@ -17,6 +17,7 @@
 
 package org.killbill.billing.payment.dispatcher;
 
+import java.util.Map;
 import java.util.Random;
 import java.util.concurrent.Callable;
 
@@ -26,6 +27,7 @@ import org.apache.shiro.util.ThreadContext;
 import org.killbill.billing.util.UUIDs;
 import org.killbill.commons.request.Request;
 import org.killbill.commons.request.RequestData;
+import org.slf4j.MDC;
 
 public class CallableWithRequestData<T> implements Callable<T> {
 
@@ -33,13 +35,20 @@ public class CallableWithRequestData<T> implements Callable<T> {
     private final Random random;
     private final SecurityManager securityManager;
     private final Subject subject;
+    private final Map<String, String> mdcContextMap;
     private final Callable<T> delegate;
 
-    public CallableWithRequestData(final RequestData requestData, final Random random, final SecurityManager securityManager, final Subject subject, final Callable<T> delegate) {
+    public CallableWithRequestData(final RequestData requestData,
+                                   final Random random,
+                                   final SecurityManager securityManager,
+                                   final Subject subject,
+                                   final Map<String, String> mdcContextMap,
+                                   final Callable<T> delegate) {
         this.requestData = requestData;
         this.random = random;
         this.securityManager = securityManager;
         this.subject = subject;
+        this.mdcContextMap = mdcContextMap;
         this.delegate = delegate;
     }
 
@@ -50,12 +59,14 @@ public class CallableWithRequestData<T> implements Callable<T> {
             UUIDs.setRandom(random);
             ThreadContext.bind(securityManager);
             ThreadContext.bind(subject);
+            MDC.setContextMap(mdcContextMap);
             return delegate.call();
         } finally {
             Request.resetPerThreadRequestData();
             UUIDs.setRandom(null);
             ThreadContext.unbindSecurityManager();
             ThreadContext.unbindSubject();
+            MDC.clear();
         }
     }
 }
diff --git a/payment/src/main/java/org/killbill/billing/payment/dispatcher/PaymentPluginDispatcher.java b/payment/src/main/java/org/killbill/billing/payment/dispatcher/PaymentPluginDispatcher.java
index 8454892..fec3c2e 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dispatcher/PaymentPluginDispatcher.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dispatcher/PaymentPluginDispatcher.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
@@ -48,20 +48,20 @@ public class PaymentPluginDispatcher {
             log.debug("Successful plugin(s) call of {} for account {} with result {}", pluginNames, accountId, result);
             return result;
         } catch (final TimeoutException e) {
-            final String errorMessage = String.format("TimeoutException while executing the plugin(s) %s", pluginNames);
-            log.warn(errorMessage, e);
+            final String errorMessage = String.format("TimeoutException while executing plugin='%s'", pluginNames);
+            log.warn(errorMessage);
             throw new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_TIMEOUT, accountId, errorMessage);
         } catch (final InterruptedException e) {
             Thread.currentThread().interrupt();
-            final String errorMessage = String.format("InterruptedException while executing the following plugin(s): %s", pluginNames);
+            final String errorMessage = String.format("InterruptedException while executing plugin='%s'", pluginNames);
             log.warn(errorMessage, e);
             throw new PaymentApiException(ErrorCode.PAYMENT_INTERNAL_ERROR, MoreObjects.firstNonNull(e.getMessage(), errorMessage));
         } catch (final ExecutionException e) {
             if (e.getCause() instanceof PaymentApiException) {
                 throw (PaymentApiException) e.getCause();
             } else if (e.getCause() instanceof LockFailedException) {
-                final String format = String.format("Failed to lock account %s", accountExternalKey);
-                log.error(format, e);
+                final String format = String.format("Failed to lock accountExternalKey='%s'", accountExternalKey);
+                log.warn(format);
                 throw new PaymentApiException(ErrorCode.PAYMENT_INTERNAL_ERROR, format);
             } else {
                 throw new PaymentApiException(e, ErrorCode.PAYMENT_INTERNAL_ERROR, MoreObjects.firstNonNull(e.getMessage(), ""));
diff --git a/payment/src/main/java/org/killbill/billing/payment/dispatcher/PluginDispatcher.java b/payment/src/main/java/org/killbill/billing/payment/dispatcher/PluginDispatcher.java
index fa65e1b..c31bbbe 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dispatcher/PluginDispatcher.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dispatcher/PluginDispatcher.java
@@ -31,6 +31,7 @@ import org.killbill.billing.util.UUIDs;
 import org.killbill.commons.profiling.Profiling;
 import org.killbill.commons.profiling.ProfilingData;
 import org.killbill.commons.request.Request;
+import org.slf4j.MDC;
 
 import com.google.common.annotations.VisibleForTesting;
 
@@ -62,6 +63,7 @@ public class PluginDispatcher<ReturnType> {
                                                                                                                      UUIDs.getRandom(),
                                                                                                                      ThreadContext.getSecurityManager(),
                                                                                                                      ThreadContext.getSubject(),
+                                                                                                                     MDC.getCopyOfContextMap(),
                                                                                                                      task);
 
         final Future<PluginDispatcherReturnType<ReturnType>> future = pluginExecutor.submit(callableWithRequestData);
diff --git a/payment/src/main/java/org/killbill/billing/payment/glue/DefaultPaymentService.java b/payment/src/main/java/org/killbill/billing/payment/glue/DefaultPaymentService.java
index 93603b1..e27a8b2 100644
--- a/payment/src/main/java/org/killbill/billing/payment/glue/DefaultPaymentService.java
+++ b/payment/src/main/java/org/killbill/billing/payment/glue/DefaultPaymentService.java
@@ -77,7 +77,7 @@ public class DefaultPaymentService implements PaymentService {
             eventBus.register(paymentBusEventHandler);
             eventBus.register(tagHandler);
         } catch (final PersistentBus.EventBusException e) {
-            log.error("Unable to register with the EventBus!", e);
+            log.error("Failed to register bus handlers", e);
         }
         paymentExecutors.initialize();
         retryService.initialize();
@@ -96,7 +96,7 @@ public class DefaultPaymentService implements PaymentService {
             eventBus.unregister(paymentBusEventHandler);
             eventBus.unregister(tagHandler);
         } catch (final PersistentBus.EventBusException e) {
-            throw new RuntimeException("Unable to unregister to the EventBus!", e);
+            throw new RuntimeException("Failed to unregister bus handlers", e);
         }
         retryService.stop();
         janitor.stop();
diff --git a/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java b/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java
index 412a211..1bf13ea 100644
--- a/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java
+++ b/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java
@@ -155,16 +155,16 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
                     existingInvoicePayment = invoiceApi.getInvoicePaymentForAttempt(paymentControlContext.getPaymentId(), internalContext);
                     if (existingInvoicePayment != null && existingInvoicePayment.isSuccess()) {
                         // Only one successful purchase per payment (the invoice could be linked to multiple successful payments though)
-                        log.info("onSuccessCall was already completed for payment purchase: " + paymentControlContext.getPaymentId());
+                        log.info("onSuccessCall was already completed for purchase paymentId='{}'", paymentControlContext.getPaymentId());
                     } else {
                         final BigDecimal invoicePaymentAmount;
                         if (paymentControlContext.getCurrency() == paymentControlContext.getProcessedCurrency()) {
                             invoicePaymentAmount = paymentControlContext.getProcessedAmount();
                         } else {
-                            log.warn("Currency {} of invoice payment {} doesn't match invoice currency {}, assuming it is a full payment" , paymentControlContext.getProcessedCurrency(), paymentControlContext.getPaymentId(), paymentControlContext.getCurrency());
+                            log.warn("processedCurrency='{}' of invoice paymentId='{}' doesn't match invoice currency='{}', assuming it is a full payment" , paymentControlContext.getProcessedCurrency(), paymentControlContext.getPaymentId(), paymentControlContext.getCurrency());
                             invoicePaymentAmount = paymentControlContext.getAmount();
                         }
-                        log.debug("Notifying invoice of successful payment: id={}, amount={}, currency={}, invoiceId={}", paymentControlContext.getPaymentId(), invoicePaymentAmount, paymentControlContext.getCurrency(), invoiceId);
+                        log.debug("Notifying invoice of successful paymentId='{}', amount='{}', currency='{}', invoiceId='{}'", paymentControlContext.getPaymentId(), invoicePaymentAmount, paymentControlContext.getCurrency(), invoiceId);
                         invoiceApi.recordPaymentAttemptCompletion(invoiceId,
                                                                   invoicePaymentAmount,
                                                                   paymentControlContext.getCurrency(),
@@ -188,7 +188,7 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
                     existingInvoicePayment = invoiceApi.getInvoicePaymentForChargeback(paymentControlContext.getPaymentId(), internalContext);
                     if (existingInvoicePayment != null) {
                         // We don't support partial chargebacks (yet?)
-                        log.info("onSuccessCall was already completed for payment chargeback: " + paymentControlContext.getPaymentId());
+                        log.info("onSuccessCall was already completed for chargeback paymentId='{}'", paymentControlContext.getPaymentId());
                     } else {
                         final InvoicePayment linkedInvoicePayment = invoiceApi.getInvoicePaymentForAttempt(paymentControlContext.getPaymentId(), internalContext);
 
@@ -213,7 +213,7 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
                     throw new IllegalStateException("Unexpected transactionType " + transactionType);
             }
         } catch (final InvoiceApiException e) {
-            log.error("InvoicePaymentControlPluginApi onSuccessCall failed for attemptId = " + paymentControlContext.getAttemptPaymentId() + ", transactionType  = " + transactionType, e);
+            log.warn("onSuccessCall failed for attemptId='{}', transactionType='{}'", paymentControlContext.getAttemptPaymentId(), transactionType, e);
         }
 
         return new DefaultOnSuccessPaymentControlResult();
@@ -575,15 +575,13 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
     private BigDecimal validateAndComputePaymentAmount(final Invoice invoice, @Nullable final BigDecimal inputAmount, final boolean isApiPayment) {
 
         if (invoice.getBalance().compareTo(BigDecimal.ZERO) <= 0) {
-            log.info("Invoice " + invoice.getId() + " has already been paid");
+            log.info("invoiceId='{}' has already been paid", invoice.getId());
             return BigDecimal.ZERO;
         }
         if (isApiPayment &&
             inputAmount != null &&
             invoice.getBalance().compareTo(inputAmount) < 0) {
-            log.info("Invoice " + invoice.getId() +
-                     " has a balance of " + invoice.getBalance().floatValue() +
-                     " less than retry payment amount of " + inputAmount.floatValue());
+            log.info("invoiceId='{}' has a balance='{}' < retry paymentAmount='{}'", invoice.getId(), invoice.getBalance().floatValue(), inputAmount.floatValue());
             return BigDecimal.ZERO;
         }
         if (inputAmount == null) {
diff --git a/payment/src/main/java/org/killbill/billing/payment/provider/DefaultPaymentControlProviderPluginRegistry.java b/payment/src/main/java/org/killbill/billing/payment/provider/DefaultPaymentControlProviderPluginRegistry.java
index 40a3ad5..9ebbb2e 100644
--- a/payment/src/main/java/org/killbill/billing/payment/provider/DefaultPaymentControlProviderPluginRegistry.java
+++ b/payment/src/main/java/org/killbill/billing/payment/provider/DefaultPaymentControlProviderPluginRegistry.java
@@ -41,13 +41,13 @@ public class DefaultPaymentControlProviderPluginRegistry implements OSGIServiceR
 
     @Override
     public void registerService(final OSGIServiceDescriptor desc, final PaymentControlPluginApi service) {
-        log.info("DefaultPaymentControlProviderPluginRegistry registering service " + desc.getRegistrationName());
+        log.info("Registering service='{}'", desc.getRegistrationName());
         pluginsByName.put(desc.getRegistrationName(), service);
     }
 
     @Override
     public void unregisterService(final String serviceName) {
-        log.info("DefaultPaymentControlProviderPluginRegistry unregistering service " + serviceName);
+        log.info("Unregistering service='{}'", serviceName);
         pluginsByName.remove(serviceName);
     }
 
diff --git a/payment/src/main/java/org/killbill/billing/payment/provider/DefaultPaymentProviderPluginRegistry.java b/payment/src/main/java/org/killbill/billing/payment/provider/DefaultPaymentProviderPluginRegistry.java
index 5d9c44e..ce8d315 100644
--- a/payment/src/main/java/org/killbill/billing/payment/provider/DefaultPaymentProviderPluginRegistry.java
+++ b/payment/src/main/java/org/killbill/billing/payment/provider/DefaultPaymentProviderPluginRegistry.java
@@ -45,13 +45,13 @@ public class DefaultPaymentProviderPluginRegistry implements OSGIServiceRegistra
 
     @Override
     public void registerService(final OSGIServiceDescriptor desc, final PaymentPluginApi service) {
-        log.info("DefaultPaymentProviderPluginRegistry registering service " + desc.getRegistrationName());
+        log.info("Registering service='{}'", desc.getRegistrationName());
         pluginsByName.put(desc.getRegistrationName(), service);
     }
 
     @Override
     public void unregisterService(final String serviceName) {
-        log.info("DefaultPaymentProviderPluginRegistry unregistering service " + serviceName);
+        log.info("Unregistering service='{}'", serviceName);
         pluginsByName.remove(serviceName);
     }
 
diff --git a/payment/src/main/java/org/killbill/billing/payment/retry/BaseRetryService.java b/payment/src/main/java/org/killbill/billing/payment/retry/BaseRetryService.java
index 7757b65..ded591c 100644
--- a/payment/src/main/java/org/killbill/billing/payment/retry/BaseRetryService.java
+++ b/payment/src/main/java/org/killbill/billing/payment/retry/BaseRetryService.java
@@ -124,10 +124,10 @@ public abstract class BaseRetryService implements RetryService {
                     }
                 }
             } catch (final NoSuchNotificationQueue e) {
-                log.error(String.format("Failed to retrieve notification queue %s:%s", DefaultPaymentService.SERVICE_NAME, getQueueName()));
+                log.error("Failed to retrieve notification queue='{}', service='{}'", getQueueName(), DefaultPaymentService.SERVICE_NAME);
                 return false;
             } catch (final IOException e) {
-                log.error(String.format("Failed to serialize notificationQueue event for objectId %s", objectId));
+                log.error("Failed to serialize notificationQueue event for objectId='{}'", objectId);
                 return false;
             }
             return true;
diff --git a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
index cfa4e43..bb8c247 100644
--- a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
+++ b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
@@ -1202,11 +1202,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
 
                 assertNotNull(thrownException);
 
-                Throwable operationException = thrownException.getCause();
-                assertNotNull(operationException);
-                assertTrue(operationException instanceof OperationException);
-
-                Throwable timeoutException = operationException.getCause();
+                Throwable timeoutException = thrownException.getCause();
                 assertNotNull(timeoutException);
                 assertTrue(timeoutException instanceof TimeoutException);
 
diff --git a/payment/src/test/java/org/killbill/billing/payment/dispatcher/TestPluginDispatcher.java b/payment/src/test/java/org/killbill/billing/payment/dispatcher/TestPluginDispatcher.java
index f2cd9c8..b582f33 100644
--- a/payment/src/test/java/org/killbill/billing/payment/dispatcher/TestPluginDispatcher.java
+++ b/payment/src/test/java/org/killbill/billing/payment/dispatcher/TestPluginDispatcher.java
@@ -30,6 +30,7 @@ import org.killbill.billing.payment.dispatcher.PluginDispatcher.PluginDispatcher
 import org.killbill.billing.util.UUIDs;
 import org.killbill.commons.request.Request;
 import org.killbill.commons.request.RequestData;
+import org.slf4j.MDC;
 import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
@@ -140,6 +141,7 @@ public class TestPluginDispatcher extends PaymentTestSuiteNoDB {
                                                                                                                                                      UUIDs.getRandom(),
                                                                                                                                                      null,
                                                                                                                                                      null,
+                                                                                                                                                     MDC.getCopyOfContextMap(),
                                                                                                                                                      delegate);
 
         final String actualRequestId = stringPluginDispatcher.dispatchWithTimeout(callable, 100, TimeUnit.MILLISECONDS);
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/DefaultServerService.java b/profiles/killbill/src/main/java/org/killbill/billing/server/DefaultServerService.java
index 0306248..a085bfa 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/DefaultServerService.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/DefaultServerService.java
@@ -55,7 +55,7 @@ public class DefaultServerService implements ServerService {
         try {
             bus.register(pushNotificationListener);
         } catch (final EventBusException e) {
-            log.warn("Failed to initialize Server service :", e);
+            log.warn("Failed to register PushNotificationListener", e);
         }
     }
 
@@ -64,7 +64,7 @@ public class DefaultServerService implements ServerService {
         try {
             bus.unregister(pushNotificationListener);
         } catch (final EventBusException e) {
-            log.warn("Failed to stop Server service :", e);
+            log.warn("Failed to unregister PushNotificationListener", e);
         }
     }
 }
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/filters/KillbillMDCInsertingServletFilter.java b/profiles/killbill/src/main/java/org/killbill/billing/server/filters/KillbillMDCInsertingServletFilter.java
new file mode 100644
index 0000000..7782a51
--- /dev/null
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/filters/KillbillMDCInsertingServletFilter.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2016 Groupon, Inc
+ * Copyright 2016 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.server.filters;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.killbill.commons.request.Request;
+import org.killbill.commons.request.RequestData;
+import org.slf4j.MDC;
+
+import com.google.inject.Singleton;
+import com.sun.jersey.spi.container.ContainerRequest;
+import com.sun.jersey.spi.container.ContainerRequestFilter;
+import com.sun.jersey.spi.container.ContainerResponse;
+import com.sun.jersey.spi.container.ContainerResponseFilter;
+import com.sun.jersey.spi.container.ContainerResponseWriter;
+
+import static org.killbill.billing.util.callcontext.InternalCallContextFactory.MDC_KB_ACCOUNT_RECORD_ID;
+import static org.killbill.billing.util.callcontext.InternalCallContextFactory.MDC_KB_TENANT_RECORD_ID;
+
+@Singleton
+public class KillbillMDCInsertingServletFilter implements ContainerRequestFilter, ContainerResponseFilter {
+
+    private static final String MDC_REQUEST_ID = "req.requestId";
+
+    @Override
+    public ContainerRequest filter(final ContainerRequest request) {
+        final RequestData perThreadRequestData = Request.getPerThreadRequestData();
+        if (perThreadRequestData != null) {
+            MDC.put(MDC_REQUEST_ID, perThreadRequestData.getRequestId());
+        }
+
+        return request;
+    }
+
+    @Override
+    public ContainerResponse filter(final ContainerRequest request, final ContainerResponse response) {
+        response.setContainerResponseWriter(new Adapter(response.getContainerResponseWriter()));
+        return response;
+    }
+
+    private static final class Adapter implements ContainerResponseWriter {
+
+        private final ContainerResponseWriter crw;
+
+        Adapter(final ContainerResponseWriter containerResponseWriter) {
+            this.crw = containerResponseWriter;
+        }
+
+        @Override
+        public OutputStream writeStatusAndHeaders(final long contentLength, final ContainerResponse response) throws IOException {
+            return crw.writeStatusAndHeaders(contentLength, response);
+        }
+
+        @Override
+        public void finish() throws IOException {
+            crw.finish();
+
+            // Removing possibly inexistent item is OK
+            MDC.remove(MDC_REQUEST_ID);
+
+            // Cleanup
+            MDC.remove(MDC_KB_ACCOUNT_RECORD_ID);
+            MDC.remove(MDC_KB_TENANT_RECORD_ID);
+        }
+    }
+}
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/filters/ProfilingContainerResponseFilter.java b/profiles/killbill/src/main/java/org/killbill/billing/server/filters/ProfilingContainerResponseFilter.java
index 38402b5..9f7e05f 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/filters/ProfilingContainerResponseFilter.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/filters/ProfilingContainerResponseFilter.java
@@ -63,7 +63,7 @@ public class ProfilingContainerResponseFilter implements ContainerRequestFilter,
                     profilingData.addStart(ProfilingFeatureType.JAXRS, request.getPath());
                 }
             } catch (IllegalArgumentException e) {
-                log.info("Profiling data output " + profilingHeaderRequest + " is not supported, profiling NOT enabled");
+                log.info("Profiling data output {} is not supported, profiling NOT enabled", profilingHeaderRequest);
             }
         }
         return request;
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/filters/RequestDataFilter.java b/profiles/killbill/src/main/java/org/killbill/billing/server/filters/RequestDataFilter.java
index 02fa786..33a7879 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/filters/RequestDataFilter.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/filters/RequestDataFilter.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
@@ -17,6 +17,8 @@
 
 package org.killbill.billing.server.filters;
 
+import java.io.IOException;
+import java.io.OutputStream;
 import java.util.List;
 
 import javax.ws.rs.core.HttpHeaders;
@@ -30,6 +32,7 @@ import com.sun.jersey.spi.container.ContainerRequest;
 import com.sun.jersey.spi.container.ContainerRequestFilter;
 import com.sun.jersey.spi.container.ContainerResponse;
 import com.sun.jersey.spi.container.ContainerResponseFilter;
+import com.sun.jersey.spi.container.ContainerResponseWriter;
 
 @Singleton
 public class RequestDataFilter implements ContainerRequestFilter, ContainerResponseFilter {
@@ -48,7 +51,7 @@ public class RequestDataFilter implements ContainerRequestFilter, ContainerRespo
 
     @Override
     public ContainerResponse filter(final ContainerRequest request, final ContainerResponse response) {
-        Request.resetPerThreadRequestData();
+        response.setContainerResponseWriter(new Adapter(response.getContainerResponseWriter()));
         return response;
     }
 
@@ -59,4 +62,26 @@ public class RequestDataFilter implements ContainerRequestFilter, ContainerRespo
         }
         return requestIds;
     }
+
+    private static final class Adapter implements ContainerResponseWriter {
+
+        private final ContainerResponseWriter crw;
+
+        Adapter(final ContainerResponseWriter containerResponseWriter) {
+            this.crw = containerResponseWriter;
+        }
+
+        @Override
+        public OutputStream writeStatusAndHeaders(final long contentLength, final ContainerResponse response) throws IOException {
+            return crw.writeStatusAndHeaders(contentLength, response);
+        }
+
+        @Override
+        public void finish() throws IOException {
+            crw.finish();
+
+            // Reset the per-thread RequestData last
+            Request.resetPerThreadRequestData();
+        }
+    }
 }
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/listeners/KillbillGuiceListener.java b/profiles/killbill/src/main/java/org/killbill/billing/server/listeners/KillbillGuiceListener.java
index e031a5d..5ec3889 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/listeners/KillbillGuiceListener.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/listeners/KillbillGuiceListener.java
@@ -27,6 +27,7 @@ import org.killbill.billing.jaxrs.resources.JaxRsResourceBase;
 import org.killbill.billing.jaxrs.util.KillbillEventHandler;
 import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.platform.config.DefaultKillbillConfigSource;
+import org.killbill.billing.server.filters.KillbillMDCInsertingServletFilter;
 import org.killbill.billing.server.filters.ProfilingContainerResponseFilter;
 import org.killbill.billing.server.filters.RequestDataFilter;
 import org.killbill.billing.server.filters.ResponseCorsFilter;
@@ -39,6 +40,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.helpers.MDCInsertingServletFilter;
 import com.google.common.collect.ImmutableMap;
 import com.google.inject.Module;
 import com.google.inject.servlet.ServletModule;
@@ -63,10 +65,14 @@ public class KillbillGuiceListener extends KillbillPlatformGuiceListener {
                                                                              // Swagger integration
                                                                              .addJaxrsResource("com.wordnik.swagger.jersey.listing");
 
-        //
-        // Add jersey filters which are executed prior jersey write the output stream
-        //
-        builder.addJerseyFilter("com.sun.jersey.api.container.filter.LoggingFilter");
+        // Set the per-thread RequestData first
+        builder.addJerseyFilter(RequestDataFilter.class.getName());
+
+        // Logback default MDC
+        builder.addJerseyFilter(MDCInsertingServletFilter.class.getName());
+
+        // Kill Bill specific MDC
+        builder.addJerseyFilter(KillbillMDCInsertingServletFilter.class.getName());
 
         // Disable WADL - it generates noisy log messages, such as:
         // c.s.j.s.w.g.AbstractWadlGeneratorGrammarGenerator - Couldn't find grammar element for class javax.ws.rs.core.Response
@@ -79,16 +85,19 @@ public class KillbillGuiceListener extends KillbillPlatformGuiceListener {
             builder.addJerseyFilter(GZIPContentEncodingFilter.class.getName());
         }
         builder.addJerseyFilter(ProfilingContainerResponseFilter.class.getName());
-        builder.addJerseyFilter(RequestDataFilter.class.getName());
 
         // Broader, to support the "Try it out!" feature
         //builder.addFilter("/" + SWAGGER_PATH + "*", ResponseCorsFilter.class);
         builder.addFilter("/*", ResponseCorsFilter.class);
 
-        // Add TenantFilter right after is multi-tenancy has been configured.
+        // Add TenantFilter right after if multi-tenancy has been configured.
         if (config.isMultiTenancyEnabled()) {
             builder.addFilter("/*", TenantFilter.class);
         }
+
+        // Finally, just before the request starts, enable the LoggingFilter
+        builder.addJerseyFilter("com.sun.jersey.api.container.filter.LoggingFilter");
+
         return builder.build();
     }
 
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/notifications/PushNotificationListener.java b/profiles/killbill/src/main/java/org/killbill/billing/server/notifications/PushNotificationListener.java
index d821177..7e3a993 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/notifications/PushNotificationListener.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/notifications/PushNotificationListener.java
@@ -113,7 +113,7 @@ public class PushNotificationListener {
                     });
             response = futureStatus.get(timeoutSec, TimeUnit.SECONDS);
         } catch (final Exception e) {
-            log.warn(String.format("Failed to push notification %s for the tenant %s", url, tenantId), e);
+            log.warn("Failed to push notification url='{}', tenantId='{}'", url, tenantId, e);
             return false;
         }
         return response.getStatusCode() >= 200 && response.getStatusCode() < 300;
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/security/TenantFilter.java b/profiles/killbill/src/main/java/org/killbill/billing/server/security/TenantFilter.java
index 7f2a2bb..8ce5b3e 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/security/TenantFilter.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/security/TenantFilter.java
@@ -37,6 +37,7 @@ import org.apache.shiro.authc.UsernamePasswordToken;
 import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
 import org.apache.shiro.realm.Realm;
 import org.killbill.billing.jaxrs.resources.JaxrsResource;
+import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.server.listeners.KillbillGuiceListener;
 import org.killbill.billing.tenant.api.Tenant;
 import org.killbill.billing.tenant.api.TenantApiException;
@@ -55,6 +56,8 @@ public class TenantFilter implements Filter {
     private static final Logger log = LoggerFactory.getLogger(TenantFilter.class);
 
     @Inject
+    protected Context context;
+    @Inject
     protected TenantUserApi tenantUserApi;
     @Inject
     protected KillbillJdbcTenantRealm killbillJdbcTenantRealm;
@@ -101,10 +104,13 @@ public class TenantFilter implements Filter {
             final Tenant tenant = tenantUserApi.getTenantByApiKey(apiKey);
             request.setAttribute(TENANT, tenant);
 
+            // Create a dummy context, to set the MDC very early for LoggingFilter
+            context.createContext(request);
+
             chain.doFilter(request, response);
         } catch (final TenantApiException e) {
             // Should never happen since Shiro validated the credentials?
-            log.warn("Couldn't find the tenant?", e);
+            log.error("Couldn't find the tenant? - should never happen!", e);
         }
     }
 
diff --git a/profiles/killbill/src/main/resources/logback.xml b/profiles/killbill/src/main/resources/logback.xml
index 3e86876..727aae2 100644
--- a/profiles/killbill/src/main/resources/logback.xml
+++ b/profiles/killbill/src/main/resources/logback.xml
@@ -21,7 +21,8 @@
 
     <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
         <encoder>
-            <pattern>%date [%thread] %-5level %logger{36} - %maskedMsg%n%ex</pattern>
+            <!-- See http://jira.qos.ch/browse/LOGBACK-262 -->
+            <pattern>%date{"yyyy-MM-dd'T'HH:mm:ss,SSSZ", UTC} th='%thread', xff='%X{req.xForwardedFor}', rId='%X{req.requestId}', tRId='%X{kb.tenantRecordId}', aRId='%X{kb.accountRecordId}', lvl='%level', log='%logger{0}', %maskedMsg%n%ex</pattern>
         </encoder>
     </appender>
 
@@ -178,6 +179,9 @@
         <appender-ref ref="SIFT-jdbc-connection"/>
     </logger>
 
+    <!-- See https://github.com/jOOQ/jOOQ/issues/4019 -->
+    <logger name="org.jooq.Constants" level="OFF"/>
+
     <!-- Silence verbose loggers in DEBUG mode -->
     <logger name="com.dmurph" level="OFF"/>
     <logger name="org.killbill.billing.notificationq" level="INFO"/>
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java b/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
index 4b17ee0..278959e 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
@@ -315,7 +315,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
                     }
                 }
             } catch (final CatalogApiException e) {
-                log.warn("Failed to get subscriptions, ", e);
+                log.warn("Failed to get subscriptions for bundleId='{}'", cur.getId(), e);
                 return null;
             }
         }
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/core/DefaultSubscriptionBaseService.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/core/DefaultSubscriptionBaseService.java
index 18dd082..e5e84ab 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/core/DefaultSubscriptionBaseService.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/core/DefaultSubscriptionBaseService.java
@@ -102,7 +102,7 @@ public class DefaultSubscriptionBaseService implements EventListener, Subscripti
                 @Override
                 public void handleReadyNotification(final NotificationEvent inputKey, final DateTime eventDateTime, final UUID fromNotificationQueueUserToken, final Long accountRecordId, final Long tenantRecordId) {
                     if (!(inputKey instanceof SubscriptionNotificationKey)) {
-                        log.error("SubscriptionBase service received an unexpected event type {}" + inputKey.getClass().getName());
+                        log.error("SubscriptionBase service received an unexpected event className='{}'", inputKey.getClass().getName());
                         return;
                     }
 
@@ -110,7 +110,7 @@ public class DefaultSubscriptionBaseService implements EventListener, Subscripti
                     final SubscriptionBaseEvent event = dao.getEventById(key.getEventId(), internalCallContextFactory.createInternalTenantContext(tenantRecordId, accountRecordId));
                     if (event == null) {
                         // This can be expected if the event is soft deleted (is_active = 0)
-                        log.info("Failed to extract event for notification key {}", inputKey);
+                        log.debug("Failed to extract event for notification key {}", inputKey);
                         return;
                     }
 
@@ -149,7 +149,7 @@ public class DefaultSubscriptionBaseService implements EventListener, Subscripti
         try {
             final DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) dao.getSubscriptionFromId(event.getSubscriptionId(), context);
             if (subscription == null) {
-                log.warn("Failed to retrieve subscription for id %s", event.getSubscriptionId());
+                log.warn("Error retrieving subscriptionId='{}'", event.getSubscriptionId());
                 return;
             }
             if (subscription.getActiveVersion() > event.getActiveVersion()) {
@@ -176,9 +176,9 @@ public class DefaultSubscriptionBaseService implements EventListener, Subscripti
                 eventBus.post(busEvent);
             }
         } catch (final EventBusException e) {
-            log.warn("Failed to post subscription event " + event, e);
+            log.warn("Failed to post event {}", event, e);
         } catch (final CatalogApiException e) {
-            log.warn("Failed to post subscription event " + event, e);
+            log.warn("Failed to post event {}", event, e);
         }
     }
 
@@ -195,7 +195,7 @@ public class DefaultSubscriptionBaseService implements EventListener, Subscripti
                 return true;
             }
         } catch (final SubscriptionBaseError e) {
-            log.error(String.format("Failed to insert next phase for subscription %s", subscription.getId()), e);
+            log.warn("Error inserting next phase for subscriptionId='{}'", subscription.getId(), e);
         }
 
         return false;
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
index 16e6090..41c73a0 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
@@ -272,19 +272,19 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
             public UUID inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
                 final SubscriptionModelDao subscriptionModel = entitySqlDaoWrapperFactory.become(SubscriptionSqlDao.class).getById(subscriptionId.toString(), context);
                 if (subscriptionModel == null) {
-                    log.error(String.format(ErrorCode.SUB_INVALID_SUBSCRIPTION_ID.getFormat(), subscriptionId.toString()));
+                    log.warn(String.format(ErrorCode.SUB_INVALID_SUBSCRIPTION_ID.getFormat(), subscriptionId.toString()));
                     return null;
                 }
 
                 final UUID bundleId = subscriptionModel.getBundleId();
                 if (bundleId == null) {
-                    log.error(String.format(ErrorCode.SUB_GET_NO_BUNDLE_FOR_SUBSCRIPTION.getFormat(), subscriptionId.toString()));
+                    log.warn(String.format(ErrorCode.SUB_GET_NO_BUNDLE_FOR_SUBSCRIPTION.getFormat(), subscriptionId.toString()));
                     return null;
                 }
 
                 final SubscriptionBundleModelDao bundleModel = entitySqlDaoWrapperFactory.become(BundleSqlDao.class).getById(bundleId.toString(), context);
                 if (bundleModel == null) {
-                    log.error(String.format(ErrorCode.SUB_GET_INVALID_BUNDLE_ID.getFormat(), bundleId.toString()));
+                    log.warn(String.format(ErrorCode.SUB_GET_INVALID_BUNDLE_ID.getFormat(), bundleId.toString()));
                     return null;
                 }
                 return bundleModel.getAccountId();
@@ -1082,7 +1082,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
             final DefaultSubscriptionBase upToDateSubscription = createSubscriptionWithNewEvent(subscription, immediateEvent, context);
             notifyBusOfEffectiveImmediateChange(entitySqlDaoWrapperFactory, upToDateSubscription, immediateEvent, seqId, context);
         } catch (final CatalogApiException e) {
-            log.warn("Failed to post effective event for subscription " + subscription.getId(), e);
+            log.warn("Failed to post effective event for subscriptionId='{}'", subscription.getId(), e);
         }
     }
 
@@ -1098,7 +1098,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
 
             eventBus.postFromTransaction(busEvent, entitySqlDaoWrapperFactory.getHandle().getConnection());
         } catch (final EventBusException e) {
-            log.warn("Failed to post effective event for subscription " + subscription.getId(), e);
+            log.warn("Failed to post effective event for subscriptionId='{}'", subscription.getId(), e);
         }
     }
 
@@ -1107,7 +1107,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
         try {
             eventBus.postFromTransaction(new DefaultRequestedSubscriptionEvent(subscription, nextEvent, transitionType, context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken()), entitySqlDaoWrapperFactory.getHandle().getConnection());
         } catch (final EventBusException e) {
-            log.warn("Failed to post requested change event for subscription " + subscription.getId(), e);
+            log.warn("Failed to post requested change event for subscriptionId='{}'", subscription.getId(), e);
         }
     }
 
@@ -1134,7 +1134,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
 
         final List<SubscriptionBundleModelDao> existingBundleModels = transBundleDao.getBundlesFromAccountAndKey(bundleData.getAccountId().toString(), bundleData.getExternalKey(), context);
         if (existingBundleModels.size() != 0) {
-            log.error(String.format("Attempted to create a bundle for account %s and key %s that already existed, skip...", bundleData.getAccountId().toString(), bundleData.getExternalKey()));
+            log.warn("Bundle already exists for accountId='{}', bundleExternalKey='{}'", bundleData.getAccountId(), bundleData.getExternalKey());
             return;
         }
 
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/api/TenantCacheInvalidation.java b/tenant/src/main/java/org/killbill/billing/tenant/api/TenantCacheInvalidation.java
index a49f041..da95a45 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/api/TenantCacheInvalidation.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/api/TenantCacheInvalidation.java
@@ -198,7 +198,7 @@ public class TenantCacheInvalidation {
                             try {
                                 parent.getEventBus().post(event);
                             } catch (final EventBusException e) {
-                                logger.warn("Failed post bus event " + event, e);
+                                logger.warn("Failed to post event {}", event, e);
                             }
                         }
                     } else {
diff --git a/util/src/main/java/org/killbill/billing/util/broadcast/DefaultBroadcastApi.java b/util/src/main/java/org/killbill/billing/util/broadcast/DefaultBroadcastApi.java
index 7c24739..0a1a922 100644
--- a/util/src/main/java/org/killbill/billing/util/broadcast/DefaultBroadcastApi.java
+++ b/util/src/main/java/org/killbill/billing/util/broadcast/DefaultBroadcastApi.java
@@ -51,7 +51,7 @@ public class DefaultBroadcastApi implements BroadcastApi {
             try {
                 eventBus.post(busEvent);
             } catch (final EventBusException e) {
-                logger.warn("Failed to deliver bus event ", e);
+                logger.warn("Failed to post event {}", event, e);
             }
         } else {
             final BroadcastModelDao modelDao = new BroadcastModelDao(serviceName, type, event, createdDate, createdBy);
diff --git a/util/src/main/java/org/killbill/billing/util/broadcast/DefaultBroadcastService.java b/util/src/main/java/org/killbill/billing/util/broadcast/DefaultBroadcastService.java
index 7ee1a99..a569fff 100644
--- a/util/src/main/java/org/killbill/billing/util/broadcast/DefaultBroadcastService.java
+++ b/util/src/main/java/org/killbill/billing/util/broadcast/DefaultBroadcastService.java
@@ -93,7 +93,7 @@ public class DefaultBroadcastService implements BroadcastService {
             broadcastExecutor.shutdown();
             boolean success = broadcastExecutor.awaitTermination(TERMINATION_TIMEOUT_SEC, TimeUnit.SECONDS);
             if (!success) {
-                logger.warn("BroadcastExecutor failed to complete termination within " + TERMINATION_TIMEOUT_SEC + "sec");
+                logger.warn("BroadcastExecutor failed to complete termination within {} sec", TERMINATION_TIMEOUT_SEC);
             }
         } catch (InterruptedException e) {
             Thread.currentThread().interrupt();
@@ -139,11 +139,11 @@ public class DefaultBroadcastService implements BroadcastService {
                     return;
                 }
 
+                final BroadcastInternalEvent event = new DefaultBroadcastInternalEvent(cur.getServiceName(), cur.getType(), cur.getEvent());
                 try {
-                    final BroadcastInternalEvent event = new DefaultBroadcastInternalEvent(cur.getServiceName(), cur.getType(), cur.getEvent());
                     eventBus.post(event);
                 } catch (final EventBusException e) {
-                    logger.error("Failed to send event BroadcastInternalEvent: ", e);
+                    logger.warn("Failed to post event {}", event, e);
                 } finally {
                     parent.setLatestRecordIdProcessed(cur.getRecordId());
                 }
diff --git a/util/src/main/java/org/killbill/billing/util/cache/TenantCatalogCacheLoader.java b/util/src/main/java/org/killbill/billing/util/cache/TenantCatalogCacheLoader.java
index 897e75d..b3f9305 100644
--- a/util/src/main/java/org/killbill/billing/util/cache/TenantCatalogCacheLoader.java
+++ b/util/src/main/java/org/killbill/billing/util/cache/TenantCatalogCacheLoader.java
@@ -72,11 +72,10 @@ public class TenantCatalogCacheLoader extends BaseCacheLoader {
             return null;
         }
         try {
-            log.info("Loading catalog cache for tenant " + internalTenantContext.getTenantRecordId());
+            log.info("Loading catalog cache for tenantRecordId='{}'", internalTenantContext.getTenantRecordId());
             return callback.loadCatalog(catalogXMLs, tenantRecordId);
         } catch (final CatalogApiException e) {
-            throw new IllegalStateException(String.format("Failed to de-serialize catalog for tenant %s : %s",
-                                                          internalTenantContext.getTenantRecordId(), e.getMessage()), e);
+            throw new IllegalStateException(String.format("Failed to de-serialize catalog for tenantRecordId='%s'", internalTenantContext.getTenantRecordId()), e);
         }
     }
 
diff --git a/util/src/main/java/org/killbill/billing/util/callcontext/CallContextFactory.java b/util/src/main/java/org/killbill/billing/util/callcontext/CallContextFactory.java
index 324a610..3fb5188 100644
--- a/util/src/main/java/org/killbill/billing/util/callcontext/CallContextFactory.java
+++ b/util/src/main/java/org/killbill/billing/util/callcontext/CallContextFactory.java
@@ -1,7 +1,9 @@
 /*
- * Copyright 2010-2011 Ning, Inc.
+ * Copyright 2010-2014 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
  * License.  You may obtain a copy of the License at:
  *
@@ -20,18 +22,10 @@ import java.util.UUID;
 
 import javax.annotation.Nullable;
 
-import org.joda.time.DateTime;
-
 public interface CallContextFactory {
 
     TenantContext createTenantContext(@Nullable UUID tenantId);
 
-    CallContext createCallContext(@Nullable UUID tenantId, String userName, CallOrigin callOrigin, UserType userType, UUID userToken);
-
     CallContext createCallContext(@Nullable UUID tenantId, String userName, CallOrigin callOrigin, UserType userType,
                                   String reasonCode, String comment, UUID userToken);
-
-    CallContext createCallContext(@Nullable UUID tenantId, String userName, CallOrigin callOrigin, UserType userType);
-
-    CallContext toMigrationCallContext(@Nullable CallContext callContext, DateTime createdDate, DateTime updatedDate);
 }
diff --git a/util/src/main/java/org/killbill/billing/util/callcontext/DefaultCallContextFactory.java b/util/src/main/java/org/killbill/billing/util/callcontext/DefaultCallContextFactory.java
index a8f9b22..30ba050 100644
--- a/util/src/main/java/org/killbill/billing/util/callcontext/DefaultCallContextFactory.java
+++ b/util/src/main/java/org/killbill/billing/util/callcontext/DefaultCallContextFactory.java
@@ -20,8 +20,6 @@ import java.util.UUID;
 
 import javax.annotation.Nullable;
 
-import org.joda.time.DateTime;
-
 import org.killbill.billing.callcontext.DefaultCallContext;
 import org.killbill.billing.callcontext.DefaultTenantContext;
 import org.killbill.clock.Clock;
@@ -44,23 +42,7 @@ public class DefaultCallContextFactory implements CallContextFactory {
 
     @Override
     public CallContext createCallContext(@Nullable final UUID tenantId, final String userName, final CallOrigin callOrigin,
-                                         final UserType userType, @Nullable final UUID userToken) {
-        return new DefaultCallContext(tenantId, userName, callOrigin, userType, userToken, clock);
-    }
-
-    @Override
-    public CallContext createCallContext(@Nullable final UUID tenantId, final String userName, final CallOrigin callOrigin,
                                          final UserType userType, final String reasonCode, final String comment, final UUID userToken) {
         return new DefaultCallContext(tenantId, userName, callOrigin, userType, reasonCode, comment, userToken, clock);
     }
-
-    @Override
-    public CallContext createCallContext(@Nullable final UUID tenantId, final String userName, final CallOrigin callOrigin, final UserType userType) {
-        return createCallContext(tenantId, userName, callOrigin, userType, null);
-    }
-
-    @Override
-    public CallContext toMigrationCallContext(final CallContext callContext, final DateTime createdDate, final DateTime updatedDate) {
-        return new MigrationCallContext(callContext, createdDate, updatedDate);
-    }
 }
diff --git a/util/src/main/java/org/killbill/billing/util/callcontext/InternalCallContextFactory.java b/util/src/main/java/org/killbill/billing/util/callcontext/InternalCallContextFactory.java
index 0460fd4..c16cd2e 100644
--- a/util/src/main/java/org/killbill/billing/util/callcontext/InternalCallContextFactory.java
+++ b/util/src/main/java/org/killbill/billing/util/callcontext/InternalCallContextFactory.java
@@ -36,6 +36,7 @@ import org.killbill.billing.util.cache.Cachable.CacheType;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.dao.NonEntityDao;
 import org.killbill.clock.Clock;
+import org.slf4j.MDC;
 
 import com.google.common.base.MoreObjects;
 
@@ -44,6 +45,9 @@ public class InternalCallContextFactory {
 
     public static final long INTERNAL_TENANT_RECORD_ID = 0L;
 
+    public static final String MDC_KB_ACCOUNT_RECORD_ID = "kb.accountRecordId";
+    public static final String MDC_KB_TENANT_RECORD_ID = "kb.tenantRecordId";
+
     private final ImmutableAccountInternalApi accountInternalApi;
     private final Clock clock;
     private final NonEntityDao nonEntityDao;
@@ -130,6 +134,8 @@ public class InternalCallContextFactory {
      * @return internal tenant callcontext
      */
     public InternalTenantContext createInternalTenantContext(final Long tenantRecordId, @Nullable final Long accountRecordId) {
+        populateMDCContext(accountRecordId, tenantRecordId);
+
         if (accountRecordId == null) {
             return new InternalTenantContext(tenantRecordId);
         } else {
@@ -212,12 +218,18 @@ public class InternalCallContextFactory {
     public InternalCallContext createInternalCallContext(final CallContext context) {
         // If tenant id is null, this will default to the default tenant record id (multi-tenancy disabled)
         final Long tenantRecordId = getTenantRecordIdSafe(context);
+
+        populateMDCContext(null, tenantRecordId);
+
         return new InternalCallContext(tenantRecordId, context);
     }
 
     // Used when we need to re-hydrate the callcontext with the account_record_id (when creating the account)
     public InternalCallContext createInternalCallContext(final Long accountRecordId, final InternalCallContext context) {
         final DateTimeZone accountTimeZone = getAccountTimeZone(context.getTenantRecordId(), accountRecordId);
+
+        populateMDCContext(accountRecordId, context.getTenantRecordId());
+
         return new InternalCallContext(context, accountRecordId, accountTimeZone);
     }
 
@@ -238,15 +250,26 @@ public class InternalCallContextFactory {
         final Long nonNulTenantRecordId = MoreObjects.firstNonNull(tenantRecordId, INTERNAL_TENANT_RECORD_ID);
         final DateTimeZone accountTimeZone = getAccountTimeZone(tenantRecordId, accountRecordId);
 
+        populateMDCContext(accountRecordId, nonNulTenantRecordId);
+
         return new InternalCallContext(nonNulTenantRecordId, accountRecordId, accountTimeZone, userToken, userName, callOrigin, userType, reasonCode, comment,
                                        createdDate, updatedDate);
     }
 
+    private void populateMDCContext(@Nullable final Long accountRecordId, final Long tenantRecordId) {
+        if (accountRecordId != null) {
+            MDC.put(MDC_KB_ACCOUNT_RECORD_ID, String.valueOf(accountRecordId));
+        }
+        MDC.put(MDC_KB_TENANT_RECORD_ID, String.valueOf(tenantRecordId));
+    }
+
     private DateTimeZone getAccountTimeZone(final Long tenantRecordId, @Nullable final Long accountRecordId) {
         if (accountRecordId == null || accountInternalApi == null) {
             return null;
         }
 
+        populateMDCContext(accountRecordId, tenantRecordId);
+
         final InternalTenantContext tmp = new InternalTenantContext(tenantRecordId, accountRecordId, null);
 
         try {
diff --git a/util/src/main/java/org/killbill/billing/util/customfield/dao/DefaultCustomFieldDao.java b/util/src/main/java/org/killbill/billing/util/customfield/dao/DefaultCustomFieldDao.java
index 1416fb4..6561b8f 100644
--- a/util/src/main/java/org/killbill/billing/util/customfield/dao/DefaultCustomFieldDao.java
+++ b/util/src/main/java/org/killbill/billing/util/customfield/dao/DefaultCustomFieldDao.java
@@ -146,7 +146,7 @@ public class DefaultCustomFieldDao extends EntityDaoBase<CustomFieldModelDao, Cu
         try {
             bus.postFromTransaction(customFieldEvent, entitySqlDaoWrapperFactory.getHandle().getConnection());
         } catch (final PersistentBus.EventBusException e) {
-            log.warn("Failed to post tag event for custom field " + customField.getId().toString(), e);
+            log.warn("Failed to post tag event for customFieldId='{}'", customField.getId().toString(), e);
         }
 
     }
diff --git a/util/src/main/java/org/killbill/billing/util/email/DefaultEmailSender.java b/util/src/main/java/org/killbill/billing/util/email/DefaultEmailSender.java
index 7c5c8f2..7f8bac4 100644
--- a/util/src/main/java/org/killbill/billing/util/email/DefaultEmailSender.java
+++ b/util/src/main/java/org/killbill/billing/util/email/DefaultEmailSender.java
@@ -89,7 +89,7 @@ public class DefaultEmailSender implements EmailSender {
 
             email.setSSL(config.useSSL());
 
-            log.info("Sending email to {}, cc {}, subject {}", new Object[]{to, cc, subject});
+            log.info("Sending email to='{}', cc='{}', subject='{}'", to, cc, subject);
             email.send();
         } catch (EmailException ee) {
             throw new EmailApiException(ee, ErrorCode.EMAIL_SENDING_FAILED);
diff --git a/util/src/main/java/org/killbill/billing/util/entity/dao/DefaultPaginationHelper.java b/util/src/main/java/org/killbill/billing/util/entity/dao/DefaultPaginationHelper.java
index a417357..d3ffa24 100644
--- a/util/src/main/java/org/killbill/billing/util/entity/dao/DefaultPaginationHelper.java
+++ b/util/src/main/java/org/killbill/billing/util/entity/dao/DefaultPaginationHelper.java
@@ -74,7 +74,7 @@ public class DefaultPaginationHelper {
                     maxNbRecords = Math.max(maxNbRecords, pages.getMaxNbRecords());
                 }
             } catch (final BillingExceptionBase e) {
-                log.warn("Error while searching plugin " + pluginName, e);
+                log.warn("Error while searching plugin='{}'", pluginName, e);
                 // Non-fatal, continue to search other plugins
             }
         }
diff --git a/util/src/main/java/org/killbill/billing/util/security/shiro/realm/KillBillJndiLdapRealm.java b/util/src/main/java/org/killbill/billing/util/security/shiro/realm/KillBillJndiLdapRealm.java
index 1c7c00f..ddbd3bb 100644
--- a/util/src/main/java/org/killbill/billing/util/security/shiro/realm/KillBillJndiLdapRealm.java
+++ b/util/src/main/java/org/killbill/billing/util/security/shiro/realm/KillBillJndiLdapRealm.java
@@ -137,7 +137,7 @@ public class KillBillJndiLdapRealm extends JndiLdapRealm {
             systemLdapCtx = ldapContextFactory.getSystemLdapContext();
             return findLDAPGroupsForUser(username, systemLdapCtx);
         } catch (AuthenticationException ex) {
-            log.info("LDAP authentication exception: " + ex.getLocalizedMessage());
+            log.info("LDAP authentication exception='{}'", ex.getLocalizedMessage());
             return ImmutableSet.<String>of();
         } finally {
             LdapUtils.closeContext(systemLdapCtx);
diff --git a/util/src/main/java/org/killbill/billing/util/tag/dao/DefaultTagDao.java b/util/src/main/java/org/killbill/billing/util/tag/dao/DefaultTagDao.java
index 62543e5..ed2d38c 100644
--- a/util/src/main/java/org/killbill/billing/util/tag/dao/DefaultTagDao.java
+++ b/util/src/main/java/org/killbill/billing/util/tag/dao/DefaultTagDao.java
@@ -142,7 +142,7 @@ public class DefaultTagDao extends EntityDaoBase<TagModelDao, Tag, TagApiExcepti
         try {
             bus.postFromTransaction(tagEvent, entitySqlDaoWrapperFactory.getHandle().getConnection());
         } catch (final PersistentBus.EventBusException e) {
-            log.warn("Failed to post tag event for tag " + tag.getId().toString(), e);
+            log.warn("Failed to post tag event for tagId='{}'", tag.getId().toString(), e);
         }
     }
 
diff --git a/util/src/main/java/org/killbill/billing/util/tag/dao/DefaultTagDefinitionDao.java b/util/src/main/java/org/killbill/billing/util/tag/dao/DefaultTagDefinitionDao.java
index 9039976..46128ce 100644
--- a/util/src/main/java/org/killbill/billing/util/tag/dao/DefaultTagDefinitionDao.java
+++ b/util/src/main/java/org/killbill/billing/util/tag/dao/DefaultTagDefinitionDao.java
@@ -184,7 +184,7 @@ public class DefaultTagDefinitionDao extends EntityDaoBase<TagDefinitionModelDao
                     try {
                         bus.postFromTransaction(tagDefinitionEvent, entitySqlDaoWrapperFactory.getHandle().getConnection());
                     } catch (final PersistentBus.EventBusException e) {
-                        log.warn("Failed to post tag definition creation event for tag " + tagDefinition.getId(), e);
+                        log.warn("Failed to post tag definition creation event for tagDefinitionId='{}'", tagDefinition.getId(), e);
                     }
 
                     return tagDefinition;
@@ -263,7 +263,7 @@ public class DefaultTagDefinitionDao extends EntityDaoBase<TagDefinitionModelDao
         try {
             bus.postFromTransaction(tagDefinitionEvent, entitySqlDaoWrapperFactory.getHandle().getConnection());
         } catch (final PersistentBus.EventBusException e) {
-            log.warn("Failed to post tag definition event for tag " + tagDefinition.getId().toString(), e);
+            log.warn("Failed to post tag definition event for tagDefinitionId='{}'", tagDefinition.getId().toString(), e);
         }
     }