У нас есть корпоративное приложение (развернутое как ухо), представляющее собой пакетный процессор, который создает несколько потоков для одновременной работы с элементами пакета (до максимального числа одновременных потоков). В рамках обработки он вызывает несколько веб-сервисов RESTful, развернутых в одной и той же Glassfish, но в двух доменах. Каждое приложение регистрирует свой собственный файл журнала приложения на сервере, и все журналы также поступают на сервер Splunk.
Нас попросили найти способ "пометить" каждый оператор журнала, чтобы мы могли выполнить поиск по всем журналам и получить все операторы, связанные с обработкой одного элемента пакета.
Каждый обрабатываемый элемент имеет уникальный идентификатор, поэтому добавить его в инструкции журнала процессора легко. Вопрос в том, как пометить журналы с веб-сервисов?
Одним из предложений было добавить параметр для каждого вызова веб-службы, который принимает этот идентификатор и распространяет его на каждый метод этих служб. Я думаю, что это сумасшедшая идея - вносить изменения такого типа в код, чтобы передать значение просто для регистрации.
Кто-нибудь сделал что-нибудь подобное? Любые предложения о том, как определить записи журнала, которые "принадлежат друг другу"?
Кстати, мы используем slf4j с log4j, но рассматриваем возможность использования logback.
UPDATE
Я работал над передачей идентификатора транзакции в качестве заголовка HTTP, но не могу заставить его работать. Я использую Джерси для себя и вот что у меня есть:
Класс My Processor, который помещает свое значение в MDC ведения журнала в качестве TransactionId и использует клиент REST-службы для некоторой обработки:
public Processor
{
RESTClient myRESTClient = new RESTClient("http://path/to/restService");
public void process(final Object object)
{
//Put the object ID in the logging MDC
log.debug("Putting '{}' in the MDC as the {} header value.", object.getObjectID(), "transactionID");
MDC.put("transactionID", object.getObjectID());
//Do some stuff
Object anotherObject = myRESTClient.doQuery(object.getValue());
//Do more some stuff
}
}
Мой RESTClient для доступа к службе REST. Именно здесь я извлекаю идентификатор транзакции из MDC и добавляю его в качестве заголовка к запросу:
public RESTClient
{
public Object doQuery(String value)
{
Object object = null;
try
{
Builder builder = myRestService.queryParam(PARAM_KEY_VALUE, value)
.accept(MediaType.APPLICATION_XML);
String transactionId = (String) MDC.get("transactionID");
logger.debug("Retrieved '{}' from MDC for key {}",
transactionId,
"transactionID");
if (this.getTransactionID() != null)
{
builder = builder.header("transactionID", transactionId);
}
object = builder.get(Object.class);
}
catch (Throwable ex)
{
//Do error handling
}
}
}
Мой класс ресурсов службы REST, который должен иметь идентификатор транзакции в своих заголовках запроса и помещает его в свой журнал MDC:
@Path("/myPath")
public class MyResource
{
@Context
private HttpContext httpContext;
@GET
@Produces(MediaType.APPLICATION_XML)
public Object doQuery( @QueryParam("value") String value)
{
putTransactionIdInMDC();
SubscriberAccount account = null;
try
{
//Do query stuff
}
catch (Exception ex)
{
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
}
return account;
}
private void putTransactionIdInMDC()
{
if (httpContext != null)
{
String transactionID = httpContext.getRequest()
.getHeaderValue("transactionID");
if (transactionID != null && transactionID.isEmpty())
{
/*
* It's not likely, but possible that two headers with the same
* header key were put in the request. If so, the value will be
* a comma separated list. We're using the first one.
*/
String[] strings = transactionID.split(",");
logger.debug("Header '{}' value(s): {}",
"transactionID",
strings);
MDC.put("transactionID", strings[0]);
}
else
{
logger.debug("The header '{}' was not included in the request.",
"transactionID");
}
}
else
{
logger.info("Could not get an HttpContext for the request");
}
}
}
Основываясь на моем журнале, я знаю, что транзакционный идентификатор помещается в MDC процессора и извлекается из него классом RESTClient. Однако он не передается в качестве заголовка http в службу REST. Может кто-нибудь сказать мне, почему нет?
Файл журнала процессора:
2012-04-13T17:30:36.541 MDT INFO [Ejb-Async-Thread-2] DEBUG my.package.Processor - Putting '12311497-2279-4516-af7d-cf9716f7748a' in the MDC as the transactionId header value.
2012-04-13T17:30:36.541 MDT INFO [Ejb-Async-Thread-2] DEBUG my.package.RESTClient- Retrieved '12311497-2279-4516-af7d-cf9716f7748a' from MDC for key transactionId
Файл журнала службы REST:
2012 Apr 13 17:30:36,337 MDT [http-thread-pool-80(3)] DEBUG my.package.MyResource - The header 'transactionId' was not included in the request.
ОБНОВЛЕНИЕ ВТОРОЕ
Нашел логическую ошибку в моем коде выше:
if (transactionID != null && transactionID.isEmpty())
должно было быть:
if (transactionID != null !&& transactionID.isEmpty())