15

JAX-RS アプリケーションで Logback を使用して SLF4J を使用しています...メッセージが再度エンコードされず、そのままログファイルに出力されるように JSON にログを記録したい:

現時点では、次のようになっています。

{"@timestamp":1363834123012,"@message":"{\"text\":\"From MLK to Barack 
Ob...\n\"}"

しかし、私はこれが欲しい:

  {"@timestamp":1363834123012,"@message": { "text ": "From MLK to Barack 
Ob...\n\}

その理由は、JSON を再度解析し、データのエスケープ解除を回避したいからです。

カスタム ログバック エンコーダーを作成しましたが、エスケープを回避する方法が見つかりませんでした。オブジェクトを logback に渡して、オブジェクトのタイプに基づいて設定を変更できますか?

編集:SSCEに要求された方法を見つけました-正確にはエレガントではありません-

私のアプリケーションでは

// SLF4J Logger
private static Logger logger = LoggerFactory.getLogger(MyClass.class);
// A logback? Marker
private Marker foo = MarkerFactory.getMarker("foo");
// Jackson ObjectMapper()
ObjectMapper mapper = new ObjectMapper();

// Log something... 
logger.info(foo, mapper.writeValueAsString(json));

ここにある Logstash-Encoder のバリエーションを使用しました: https://github.com/logstash/logstash-logback-encoder

package my.package;

import static org.apache.commons.io.IOUtils.*;

import java.io.IOException;
import java.util.Map;
import java.util.Map.Entry;

import org.codehaus.jackson.JsonGenerator.Feature;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.node.ObjectNode;
import org.slf4j.Marker;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.IThrowableProxy;
import ch.qos.logback.classic.spi.ThrowableProxyUtil;
import ch.qos.logback.core.CoreConstants;
import ch.qos.logback.core.encoder.EncoderBase;

public class JsonEncoder extends EncoderBase<ILoggingEvent> {

    private static final ObjectMapper MAPPER = new ObjectMapper().configure(
        Feature.ESCAPE_NON_ASCII, true);
    private static Marker M;

    private boolean immediateFlush = true;

@Override
public void doEncode(ILoggingEvent event) throws IOException {

    M = event.getMarker();

    ObjectNode eventNode = MAPPER.createObjectNode();

    eventNode.put("@timestamp", event.getTimeStamp());
    //
    if (M != null) {
        if (M.getName().equals("foo")) {
            JsonNode j = MAPPER.readTree(event.getFormattedMessage());
            eventNode.put("@foo", j);
        }
    } else {
        eventNode.put("@message", event.getFormattedMessage());
    }
    eventNode.put("@fields", createFields(event));

    write(MAPPER.writeValueAsBytes(eventNode), outputStream);
    write(CoreConstants.LINE_SEPARATOR, outputStream);

    if (immediateFlush) {
        outputStream.flush();
    }

}

private ObjectNode createFields(ILoggingEvent event) {
         // not important here
    return fieldsNode;

}

@Override
public void close() throws IOException {
    write(LINE_SEPARATOR, outputStream);
}

public boolean isImmediateFlush() {
    return immediateFlush;
}

public void setImmediateFlush(boolean immediateFlush) {
    this.immediateFlush = immediateFlush;
}
}

それは今動作します!うん!しかし、それは最善の方法ではないと思います(シリアル化、JSONの逆シリアル化...)

4

7 に答える 7

4

RawJsonAppendingMarker を使用します。

log.trace(net.logstash.logback.marker.Markers.appendRaw("jsonMessage", jsonString), null);
于 2016-01-05T12:30:14.517 に答える
3

I ran into the same problem. I solved it with

<encoder
 class="net.logstash.logback.encoder.LogstashEncoder">          
</encoder

instead of

<encoder
 class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">

In my java code I used:

SRV_PERF_LOGGER.info(net.logstash.logback.marker.Markers.appendRaw("message", jackson.writeValueAsString(dto)), null);
于 2016-01-29T14:21:41.400 に答える
2

これは、ログを json 形式でファイルにダンプし、コンソールで行をデバッグする、更新された (2016) groovy logback 構成です。理解するのに一日中かかったので、スレッドを更新すると思いました。

    import ch.qos.logback.classic.encoder.PatternLayoutEncoder
import ch.qos.logback.core.ConsoleAppender
import ch.qos.logback.core.rolling.FixedWindowRollingPolicy
import ch.qos.logback.core.rolling.RollingFileAppender
import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy
import net.logstash.logback.encoder.LogstashEncoder

import static ch.qos.logback.classic.Level.INFO
import static ch.qos.logback.classic.Level.WARN

def PROJECT_ID = "com.foo"

    appender("file", RollingFileAppender) {
        file = "/tmp/logs/${PROJECT_ID}.json"
        encoder(LogstashEncoder)
        rollingPolicy(FixedWindowRollingPolicy) {
            maxIndex = 1
            fileNamePattern = "logs/${PROJECT_ID}.json.%i"
        }
        triggeringPolicy(SizeBasedTriggeringPolicy) {
            maxFileSize = "1MB"
        }
    }


    appender("STDOUT", ConsoleAppender) {
        encoder(PatternLayoutEncoder) {
            pattern = "%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n"
        }
    }

    logger("com.foo", INFO, ["STDOUT", "file"], false)

    root(WARN, ["STDOUT", "file"])
于 2016-02-18T21:42:58.450 に答える
1

Logback は、JSON で異常なことは何もしません。通常どおりログに記録されるのは単なる文字列です。その形式で書き出すある種の JSON アペンダーについて話しているのでない限り、エスケープはおそらくあなたの側で起こっています。Logback自体にはそのようなものはないと確信しているので、それが問題である場合は、代わりにAppenderをどこから入手したかを調べてください。SSCCEは、さらなるトラブルシューティングに役立ちます

于 2013-03-21T03:12:37.397 に答える
0

問題の原因となっている元のコードはわかりませんが、次のようになっているのではないかと思います

JsonNode logOutput;
String messageJSONAsString;

...

logOutput.put("@message", messageJSONAsString);
logger.info(objectMapper.writeValueAsString(logOutput);

これにより、出力にエスケープされた JSON が生成されます。これは、メッセージを出力 JsonNode に入れると、Jackson がメッセージを再エスケープして、出力が有効な JSON であることを確認するためです。

ここでの解決策は、メッセージを文字列ではなく ObjectNode として出力に入れることです。通常、すでにオブジェクトとしてオブジェクトにアクセスできます。その場合は、次のことができます。

ObjectNode jsonObject = objectMapper.valueToTree(messageObject);
logOutput.put("@message", jsonObject)

それ以外の場合、メッセージが JSON 文字列の場合は、それを解析して出力に追加します

logoutput.put("@message", objectMapper.readTree(messageJSONAsString));
于 2015-02-17T22:20:05.533 に答える