Условное ведение журнала с log4j

Веб-приложение, над которым я работаю, иногда создает проблемы целостности данных для некоторых пользователей. Я хотел бы включить ведение журнала уровня трассировки, но, поскольку мы имеем дело с сотнями запросов в секунду, в журнале трассировки не может быть и речи о каждом отдельном запросе.

Есть ли способ с log4j, чтобы иметь возможность войти в систему условно? Другими словами, я хотел бы иметь возможность получать журналы трассировки только тогда, когда конкретные пользователи делают запрос. Поскольку я не знаю заранее, какие пользователи будут затронуты, я не могу просто временно жестко кодировать имена пользователей.

Редактировать:

Я думаю, что мне нужно быть немного яснее. Я могу легко добавить условия в мои записи журнала. Например

Logger logger = Logger.getLogger("foo");
String usernameFilter = "piglet";
String username = request.getParameter("username");
logger.setLevel(usernameFilter.equals(username) ? Level.TRACE : Level.INFO);
if (logger.isTraceEnabled()) {
   logger.trace("blah blah blah");
}

Сложность заключается в динамическом изменении условия, которое устанавливает уровень журнала. Другими словами, в приведенном выше примере, как я могу установить значение usernameFilter, кроме жесткого его кодирования.

4 ответа

Решение

Вы хотите посмотреть на вложенные диагностические контексты или сопоставленные диагностические контексты в log4j или slf4j. NDC/MDC позволяет вставлять в сеанс данные, которые могут быть отфильтрованы log4j.

Таким образом, вы должны определить имя пользователя, которое будет в NDC, а затем вы можете изменить log4j.properties, чтобы изменить уровень ведения журнала для конкретных пользователей.

MDC использует карту, тогда как NDC основан на принципе стека. Если вы используете slf4j, вы даже можете создавать отдельные файлы журналов в зависимости от информации в вашем MDC.

Например, мы сделали это, когда пользователи зашли на сайт. Мы хотели отследить, что делал конкретный пользователь (ретроспективно), поэтому мы добавили имя пользователя и идентификатор сеанса в NDC, а затем мы могли бы опубликовать фильтр по ним.

Код был похож на следующее:

public class LoggingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        MDC.put("username", session.getParameter("username")); // or where ever t is stored
        chain.doFilter(request, response);
    }
}

В вашем log4j.xml этот фильтр основан на пользователе:

  <appender name="UserDebug" class="org.apache.log4j.RollingFileAppender">
    <param name="File" value="userdebug.log"/>
    <param name="Append" value="true"/>
    <param name="MaxFileSize" value="5000KB"/>
    <param name="maxBackupIndex" value="5"/> 
          <layout class="org.apache.log4j.PatternLayout">
                  <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} [%t] user:%X{username} %-5p - %m%n" />
          </layout>

          <filter class="org.apache.log4j.varia.StringMatchFilter">
                  <param name="StringToMatch" value="user:fred" />
                  <param name="AcceptOnMatch" value="true" />
          </filter>

      <filter class="org.apache.log4j.varia.DenyAllFilter"/>
  </appender>

% X {ключ} выводит значение MDC.get(ключ) в MDC. Если вам нужен более сложный фильтр, вы можете расширить его самостоятельно и посмотреть значения в MDC самостоятельно.

Ответ Мэтью Фарвелла (используйте MDC) - тот, который мне помог, и он упоминает о написании вашего собственного фильтра. В некоторых случаях мне нужно было скрыть сообщения регистрации. В частности, у нас есть вызов проверки работоспособности, который срабатывает гораздо чаще, чем при обычном использовании пользователем, и он заполнял журналы без необходимости. Решение Мэтью не подходит для моей ситуации, потому что оно требует, чтобы вы добавили MDC к фактическому выводу журнала. Я только хочу использовать MDC для фильтрации, поэтому я расширил org.apache.log4j.spi.Filter со следующим классом:

/**
 * Log4J filter that stops certain log messages from being logged, based on a
 * value in the MDC (See Log4J docs).
 */
public class Log4JMDCFilter extends Filter
{

private String keyToMatch;
private String valueToMatch;
private boolean denyOnMatch = true;

/**
 * {@inheritDoc}
 */
public int decide(LoggingEvent event)
{
    if (keyToMatch != null && valueToMatch != null
        && valueToMatch.equals(event.getMDC(keyToMatch)))
    {
        return denyOnMatch ? DENY : ACCEPT;
    }

    return denyOnMatch ? ACCEPT : DENY;
}

/**
 * The key on which to filter.
 * 
 * @return key on which to filter
 */
public String getKeyToMatch()
{
    return keyToMatch;
}

/**
 * Sets the key on which to filter.
 * 
 * @param keyToMatch key on which to filter
 */
public void setKeyToMatch(String keyToMatch)
{
    this.keyToMatch = keyToMatch;
}

/**
 * Gets the value to match.
 * 
 * @return the value to match.
 */
public String getValueToMatch()
{
    return valueToMatch;
}

/**
 * Sets the value to match.
 * 
 * @param valueToMatch the value to match.
 */
public void setValueToMatch(String valueToMatch)
{
    this.valueToMatch = valueToMatch;
}

/**
 * Returns true if the log message should not be logged if a match is found.
 * 
 * @return true if the log message should not be logged if a match is found.
 */
public boolean isDenyOnMatch()
{
    return denyOnMatch;
}

/**
 * Set this to "true" if you do not want log messages that match the given
 * key/value to be logged. False if you only want messages that match to be
 * logged.
 * 
 * @param denyOnMatch "true" if you do not want log messages that match the
 *        given key/value to be logged. False if you only want messages that
 *        match to be logged.
 */
public void setDenyOnMatch(String denyOnMatch)
{
    this.denyOnMatch = Boolean.valueOf(denyOnMatch).booleanValue();
}

}

Используйте следующий фрагмент файла log4j.xml, чтобы активировать фильтр ("HEALTHCHECK" - это ключ, а "true" - это значение, по которому я фильтрую):

    <filter class="com.copart.hh.core.utils.Log4JMDCFilter">
        <param name="keyToMatch" value="HEALTHCHECK" />
        <param name="valueToMatch" value="true" />
        <param name="denyOnMatch" value="true" />
    </filter>

Затем, где бы вы ни захотели пометить для фильтрации, вставьте код, подобный следующему:

MDC.put("HEALTHCHECK", "true");
try
{    
      // do healthcheck stuff that generates unnecessary logs
}
finally
{
    MDC.remove("HEALTHCHECK"); // not sure this is strictly necessary
}

Я нашел эту статью в блоге очень полезной. Это может помочь вам создать условия для входа ваших пользователей.

Я делаю обходной путь, как следует

Сконфигурируйте свою цель журнала в log4j.xml следующим образом:

    <appender name="file" class="org.apache.log4j.RollingFileAppender">
    <param name="file" value="${catalina.home}/logs/uploader.log" />
    <param name="append" value="false" />
    <param name="threshold" value="info" />
    <layout class="org.apache.log4j.PatternLayout">
        <param name="ConversionPattern" value="%d{ISO8601} %-5p [%c{1}] - %m%n" />
    </layout>

    <filter class="org.apache.log4j.varia.StringMatchFilter">
        <param name="StringToMatch" value="trackfile" />
        <param name="AcceptOnMatch" value="true" />
    </filter>

     <filter class="org.apache.log4j.varia.DenyAllFilter" />   
</appender>

создайте метод ведения журнала клиента, который добавит ваш целевой тег [ trackfile ]

private void logfile(String msg) {
    logger.info("trackfile: " + msg);
}

зарегистрируйте свою информацию, используя вышеуказанный метод:

logfile("your log message")

введите описание изображения здесь

Другие вопросы по тегам