Skip to content

Commit

Permalink
Add fields class
Browse files Browse the repository at this point in the history
  • Loading branch information
mhalbritter committed Jun 17, 2024
1 parent 30e0b5b commit ccca40a
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 51 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* Copyright 2012-2024 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.boot.logging.json;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

/**
* Collection of {@link Field fields}.
*
* @author Moritz Halbritter
* @since 3.4.0
*/
public class Fields implements Iterable<Field> {

private final List<Field> fields = new ArrayList<>();

/**
* Adds the given field.
* @param field the field to add
*/
public void add(Field field) {
this.fields.add(field);
}

/**
* Adds the given key and value.
* @param key the key to add
* @param value the value to add
*/
public void add(Key key, Value value) {
this.fields.add(Field.of(key, value));
}

/**
* Adds all given fields.
* @param fields the fields to add
*/
public void addAll(Iterable<? extends Field> fields) {
for (Field field : fields) {
this.fields.add(field);
}
}

/**
* Adds all given fields.
* @param fields the fields to add
*/
public void addAll(Field... fields) {
this.fields.addAll(Arrays.asList(fields));
}

@Override
public Iterator<Field> iterator() {
return this.fields.iterator();
}

/**
* Creates a new instance with the given fields
* @param fields the fields
* @return the new instance
*/
public static Fields of(Iterable<? extends Field> fields) {
Fields result = new Fields();
result.addAll(fields);
return result;
}

/**
* Creates a new instance with the given fields.
* @param fields the fields
* @return the new instance
*/
public static Fields of(Field... fields) {
Fields result = new Fields();
result.addAll(fields);
return result;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,6 @@ default void setServiceEnvironment(String serviceEnvironment) {
* @param event the event to log
* @return the fields to write
*/
Iterable<Field> getFields(E event);
Fields getFields(E event);

}
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,14 @@ static void escape(char c, StringBuilder output) {
// int,
// java.lang.StringBuilder)
switch (c) {
case '"' -> output.append("\\\"");
case '\\' -> output.append("\\\\");
case '/' -> output.append("\\/");
case '\b' -> output.append("\\b");
case '\t' -> output.append("\\t");
case '\f' -> output.append("\\f");
case '\n' -> output.append("\\n");
case '\r' -> output.append("\\r");
case '"' -> output.append("\\\"");
case '\\' -> output.append("\\\\");
case '\t' -> output.append("\\t");
default -> output.append(c);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,22 @@ static Value of(long value) {
return (output) -> output.append(value);
}

/**
* Creates a value of the given {@code boolean}.
* @param value the boolean value
* @return the created value
*/
static Value of(boolean value) {
return (output) -> output.append(value);
}

/**
* Creates a value of the given {@code double}.
* @param value the double value
* @return the created value
*/
static Value of(double value) {
return (output) -> output.append(value);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
Expand All @@ -37,6 +36,7 @@
import org.slf4j.event.KeyValuePair;

import org.springframework.boot.logging.json.Field;
import org.springframework.boot.logging.json.Fields;
import org.springframework.boot.logging.json.Key;
import org.springframework.boot.logging.json.Value;
import org.springframework.util.Assert;
Expand Down Expand Up @@ -161,42 +161,42 @@ String getServiceEnvironment() {
private static final class EcsJsonFormat extends BaseLogbackJsonFormat {

@Override
public Iterable<Field> getFields(ILoggingEvent event) {
List<Field> fields = new ArrayList<>();
fields.add(Field.of(Key.verbatim("@timestamp"), Value.verbatim(event.getInstant().toString())));
fields.add(Field.of(Key.verbatim("log.level"), Value.verbatim(event.getLevel().toString())));
public Fields getFields(ILoggingEvent event) {
Fields fields = new Fields();
fields.add(Key.verbatim("@timestamp"), Value.verbatim(event.getInstant().toString()));
fields.add(Key.verbatim("log.level"), Value.verbatim(event.getLevel().toString()));
if (getPid() != null) {
fields.add(Field.of(Key.verbatim("process.pid"), Value.of(getPid())));
fields.add(Key.verbatim("process.pid"), Value.of(getPid()));
}
fields.add(Field.of(Key.verbatim("process.thread.name"), Value.escaped(event.getThreadName())));
fields.add(Key.verbatim("process.thread.name"), Value.escaped(event.getThreadName()));
if (getServiceName() != null) {
fields.add(Field.of(Key.verbatim("service.name"), Value.escaped(getServiceName())));
fields.add(Key.verbatim("service.name"), Value.escaped(getServiceName()));
}
if (getServiceVersion() != null) {
fields.add(Field.of(Key.verbatim("service.version"), Value.escaped(getServiceVersion())));
fields.add(Key.verbatim("service.version"), Value.escaped(getServiceVersion()));
}
if (getServiceEnvironment() != null) {
fields.add(Field.of(Key.verbatim("service.environment"), Value.escaped(getServiceEnvironment())));
fields.add(Key.verbatim("service.environment"), Value.escaped(getServiceEnvironment()));
}
if (getServiceNodeName() != null) {
fields.add(Field.of(Key.verbatim("service.node.name"), Value.escaped(getServiceNodeName())));
fields.add(Key.verbatim("service.node.name"), Value.escaped(getServiceNodeName()));
}
fields.add(Field.of(Key.verbatim("log.logger"), Value.escaped(event.getLoggerName())));
fields.add(Field.of(Key.verbatim("message"), Value.escaped(event.getFormattedMessage())));
fields.add(Key.verbatim("log.logger"), Value.escaped(event.getLoggerName()));
fields.add(Key.verbatim("message"), Value.escaped(event.getFormattedMessage()));
addMdc(event, fields);
addKeyValuePairs(event, fields);
IThrowableProxy throwable = event.getThrowableProxy();
if (throwable != null) {
fields.add(Field.of(Key.verbatim("error.type"), Value.verbatim(throwable.getClassName())));
fields.add(Field.of(Key.verbatim("error.message"), Value.escaped(throwable.getMessage())));
fields.add(Field.of(Key.verbatim("error.stack_trace"),
Value.escaped(getThrowableProxyConverter().convert(event))));
fields.add(Key.verbatim("error.type"), Value.verbatim(throwable.getClassName()));
fields.add(Key.verbatim("error.message"), Value.escaped(throwable.getMessage()));
fields.add(Key.verbatim("error.stack_trace"),
Value.escaped(getThrowableProxyConverter().convert(event)));
}
fields.add(Field.of(Key.verbatim("ecs.version"), Value.verbatim("8.11")));
fields.add(Key.verbatim("ecs.version"), Value.verbatim("8.11"));
return fields;
}

private void addKeyValuePairs(ILoggingEvent event, List<Field> fields) {
private void addKeyValuePairs(ILoggingEvent event, Fields fields) {
List<KeyValuePair> keyValuePairs = event.getKeyValuePairs();
if (CollectionUtils.isEmpty(keyValuePairs)) {
return;
Expand All @@ -207,7 +207,7 @@ private void addKeyValuePairs(ILoggingEvent event, List<Field> fields) {
}
}

private static void addMdc(ILoggingEvent event, List<Field> fields) {
private static void addMdc(ILoggingEvent event, Fields fields) {
Map<String, String> mdc = event.getMDCPropertyMap();
if (CollectionUtils.isEmpty(mdc)) {
return;
Expand All @@ -222,29 +222,27 @@ private static void addMdc(ILoggingEvent event, List<Field> fields) {
private static final class LogstashJsonFormat extends BaseLogbackJsonFormat {

@Override
public Iterable<Field> getFields(ILoggingEvent event) {
List<Field> fields = new ArrayList<>();
public Fields getFields(ILoggingEvent event) {
Fields fields = new Fields();
OffsetDateTime time = OffsetDateTime.ofInstant(event.getInstant(), ZoneId.systemDefault());
fields.add(Field.of(Key.verbatim("@timestamp"),
Value.verbatim(DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(time))));
fields.add(Field.of(Key.verbatim("@version"), Value.verbatim("1")));
fields.add(Field.of(Key.verbatim("message"), Value.escaped(event.getFormattedMessage())));
fields.add(Field.of(Key.verbatim("logger_name"), Value.escaped(event.getLoggerName())));
fields.add(Field.of(Key.verbatim("thread_name"), Value.escaped(event.getThreadName())));
fields.add(Field.of(Key.verbatim("level"), Value.escaped(event.getLevel().toString())));
fields.add(Field.of(Key.verbatim("level_value"), Value.of(event.getLevel().toInt())));
fields.add(Key.verbatim("@timestamp"), Value.verbatim(DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(time)));
fields.add(Key.verbatim("@version"), Value.verbatim("1"));
fields.add(Key.verbatim("message"), Value.escaped(event.getFormattedMessage()));
fields.add(Key.verbatim("logger_name"), Value.escaped(event.getLoggerName()));
fields.add(Key.verbatim("thread_name"), Value.escaped(event.getThreadName()));
fields.add(Key.verbatim("level"), Value.escaped(event.getLevel().toString()));
fields.add(Key.verbatim("level_value"), Value.of(event.getLevel().toInt()));
addMdc(event, fields);
addKeyValuePairs(event, fields);
addMarkers(event, fields);
IThrowableProxy throwable = event.getThrowableProxy();
if (throwable != null) {
fields.add(Field.of(Key.verbatim("stack_trace"),
Value.escaped(getThrowableProxyConverter().convert(event))));
fields.add(Key.verbatim("stack_trace"), Value.escaped(getThrowableProxyConverter().convert(event)));
}
return fields;
}

private void addMarkers(ILoggingEvent event, List<Field> fields) {
private void addMarkers(ILoggingEvent event, Fields fields) {
List<Marker> markers = event.getMarkerList();
if (CollectionUtils.isEmpty(markers)) {
return;
Expand All @@ -266,7 +264,7 @@ private void addTag(Marker marker, Collection<String> tags) {
}
}

private void addKeyValuePairs(ILoggingEvent event, List<Field> fields) {
private void addKeyValuePairs(ILoggingEvent event, Fields fields) {
List<KeyValuePair> keyValuePairs = event.getKeyValuePairs();
if (CollectionUtils.isEmpty(keyValuePairs)) {
return;
Expand All @@ -277,7 +275,7 @@ private void addKeyValuePairs(ILoggingEvent event, List<Field> fields) {
}
}

private static void addMdc(ILoggingEvent event, List<Field> fields) {
private static void addMdc(ILoggingEvent event, Fields fields) {
Map<String, String> mdc = event.getMDCPropertyMap();
if (CollectionUtils.isEmpty(mdc)) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,22 +137,22 @@ public byte[] headerBytes() {
public byte[] encode(ILoggingEvent event) {
StringBuilder output = new StringBuilder();
output.append('{');
boolean appendedComma = false;
for (Field field : this.format.getFields(event)) {
field.write(output);
output.append(',');
appendedComma = true;
}
if (appendedComma) {
removeTrailingComma(output);
}
removeTrailingComma(output);
output.append('}');
output.append('\n');
return output.toString().getBytes(StandardCharsets.UTF_8);
}

private void removeTrailingComma(StringBuilder output) {
int length = output.length();
char end = output.charAt(length - 1);
if (end == ',') {
output.setLength(length - 1);
}
output.setLength(output.length() - 1);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,9 @@

package smoketest.simple;

import java.util.List;

import ch.qos.logback.classic.spi.ILoggingEvent;

import org.springframework.boot.logging.json.Field;
import org.springframework.boot.logging.json.Fields;
import org.springframework.boot.logging.json.Key;
import org.springframework.boot.logging.json.Value;
import org.springframework.boot.logging.logback.LogbackJsonFormat;
Expand All @@ -33,9 +31,11 @@
class CustomJsonFormat implements LogbackJsonFormat {

@Override
public Iterable<Field> getFields(ILoggingEvent event) {
return List.of(Field.of(Key.verbatim("epoch"), Value.of(event.getInstant().toEpochMilli())),
Field.of(Key.verbatim("message"), Value.escaped(event.getFormattedMessage())));
public Fields getFields(ILoggingEvent event) {
Fields fields = new Fields();
fields.add(Key.verbatim("epoch"), Value.of(event.getInstant().toEpochMilli()));
fields.add(Key.verbatim("message"), Value.escaped(event.getFormattedMessage()));
return fields;
}

}

0 comments on commit ccca40a

Please sign in to comment.