16
16
package net .logstash .logback .marker ;
17
17
18
18
import java .io .IOException ;
19
+ import java .util .Map ;
19
20
import java .util .Objects ;
20
21
import java .util .concurrent .ConcurrentHashMap ;
21
22
28
29
import com .fasterxml .jackson .databind .JsonMappingException ;
29
30
import com .fasterxml .jackson .databind .JsonSerializer ;
30
31
import com .fasterxml .jackson .databind .ObjectMapper ;
31
- import com .fasterxml .jackson .databind .SerializationConfig ;
32
32
import com .fasterxml .jackson .databind .SerializerProvider ;
33
- import com .fasterxml .jackson .databind .ser .DefaultSerializerProvider ;
34
- import com .fasterxml .jackson .databind .ser .ResolvableSerializer ;
35
33
import com .fasterxml .jackson .databind .util .NameTransformer ;
36
34
import org .slf4j .Marker ;
37
35
@@ -91,8 +89,7 @@ public class ObjectFieldsAppendingMarker extends LogstashMarker implements Struc
91
89
*
92
90
* Since apps will typically serialize the same types of objects repeatedly, they shouldn't grow too much.
93
91
*/
94
- private static final ConcurrentHashMap <Class <?>, JsonSerializer <Object >> beanSerializers = new ConcurrentHashMap <>();
95
- private static final ConcurrentHashMap <ObjectMapper , SerializerProvider > serializerProviders = new ConcurrentHashMap <>();
92
+ private static final Map <ObjectMapper , SerializerHelper > serializerHelper = new ConcurrentHashMap <>();
96
93
97
94
public ObjectFieldsAppendingMarker (Object object ) {
98
95
super (MARKER_NAME );
@@ -101,70 +98,18 @@ public ObjectFieldsAppendingMarker(Object object) {
101
98
102
99
@ Override
103
100
public void writeTo (JsonGenerator generator ) throws IOException {
104
- if (object != null ) {
105
- ObjectMapper mapper = (ObjectMapper ) generator .getCodec ();
106
- JsonSerializer <Object > serializer = getBeanSerializer (mapper );
107
- if (serializer .isUnwrappingSerializer ()) {
108
- serializer .serialize (object , generator , getSerializerProvider (mapper ));
109
- }
101
+ if (this .object != null ) {
102
+ SerializerHelper helper = getSerializerHelper (generator );
103
+ helper .serialize (generator , this .object );
110
104
}
111
105
}
112
-
106
+
107
+
113
108
@ Override
114
109
public String toStringSelf () {
115
110
return StructuredArguments .toString (object );
116
111
}
117
112
118
- /**
119
- * Gets a serializer that will write the {@link #object} unwrapped.
120
- */
121
- private JsonSerializer <Object > getBeanSerializer (ObjectMapper mapper ) throws JsonMappingException {
122
-
123
- JsonSerializer <Object > jsonSerializer = beanSerializers .get (object .getClass ());
124
-
125
- if (jsonSerializer == null ) {
126
- SerializerProvider serializerProvider = getSerializerProvider (mapper );
127
- JsonSerializer <Object > newSerializer = mapper .getSerializerFactory ().createSerializer (
128
- serializerProvider ,
129
- mapper .getSerializationConfig ().constructType (object .getClass ()))
130
- .unwrappingSerializer (NameTransformer .NOP );
131
-
132
- if (newSerializer instanceof ResolvableSerializer ) {
133
- ((ResolvableSerializer ) newSerializer ).resolve (serializerProvider );
134
- }
135
-
136
- JsonSerializer <Object > existingSerializer = beanSerializers .putIfAbsent (
137
- object .getClass (),
138
- newSerializer );
139
-
140
- jsonSerializer = (existingSerializer == null ) ? newSerializer : existingSerializer ;
141
- }
142
- return jsonSerializer ;
143
-
144
- }
145
-
146
- /**
147
- * Gets a {@link SerializerProvider} configured with the {@link ObjectMapper}'s {@link SerializationConfig}
148
- * ({@link ObjectMapper#getSerializationConfig()}) to be used for serialization.
149
- * <p>
150
- * Note that the {@link ObjectMapper}'s {@link SerializerProvider} ({@link ObjectMapper#getSerializerProvider()})
151
- * cannot be used directly, because the {@link SerializerProvider}'s {@link SerializationConfig} ({@link SerializerProvider#getConfig()}) is null,
152
- * which causes NullPointerExceptions when it is used.
153
- */
154
- private SerializerProvider getSerializerProvider (ObjectMapper mapper ) {
155
-
156
- SerializerProvider provider = serializerProviders .get (mapper );
157
- if (provider == null ) {
158
-
159
- SerializerProvider newProvider = ((DefaultSerializerProvider ) mapper .getSerializerProvider ())
160
- .createInstance (mapper .getSerializationConfig (), mapper .getSerializerFactory ());
161
-
162
- SerializerProvider existingProvider = serializerProviders .putIfAbsent (mapper , newProvider );
163
-
164
- provider = (existingProvider == null ) ? newProvider : existingProvider ;
165
- }
166
- return provider ;
167
- }
168
113
169
114
@ Override
170
115
public boolean equals (Object obj ) {
@@ -182,6 +127,7 @@ public boolean equals(Object obj) {
182
127
return Objects .equals (this .object , other .object );
183
128
}
184
129
130
+
185
131
@ Override
186
132
public int hashCode () {
187
133
final int prime = 31 ;
@@ -190,4 +136,61 @@ public int hashCode() {
190
136
result = prime * result + (this .object == null ? 0 : this .object .hashCode ());
191
137
return result ;
192
138
}
139
+
140
+
141
+ /**
142
+ * Get a {@link SerializerHelper} suitable for use with the given {@link JsonGenerator}.
143
+ *
144
+ * @param gen the {@link JsonGenerator} for which an helper should be returned
145
+ * @return a {@link SerializerHelper}
146
+ */
147
+ private static SerializerHelper getSerializerHelper (JsonGenerator gen ) {
148
+ ObjectMapper mapper = (ObjectMapper ) gen .getCodec ();
149
+ return serializerHelper .computeIfAbsent (mapper , SerializerHelper ::new );
150
+ }
151
+
152
+ private static class SerializerHelper {
153
+ private final SerializerProvider serializers ;
154
+ private final ConcurrentHashMap <Class <?>, JsonSerializer <Object >> cache = new ConcurrentHashMap <>();
155
+
156
+ SerializerHelper (ObjectMapper mapper ) {
157
+ this .serializers = mapper .getSerializerProviderInstance ();
158
+ }
159
+
160
+ /**
161
+ * Serialize the given value using the supplied generator
162
+ *
163
+ * @param gen the {@link JsonGenerator} to use to serialize the value
164
+ * @param value the value to serialize
165
+ * @throws IOException thrown when the underlying {@link JsonGenerator} could not be created
166
+ * or when it has problems to serialize the given value
167
+ */
168
+ public void serialize (JsonGenerator gen , Object value ) throws IOException {
169
+ if (value != null ) {
170
+ JsonSerializer <Object > unwrappingSerializer = getUnwrappingSerializer (value .getClass ());
171
+
172
+ /*
173
+ * Make sure the serializer accepts to serialize in an "unwrapped" fashion.
174
+ * This may not be the case for serializer for Number types for instance.
175
+ */
176
+ if (unwrappingSerializer .isUnwrappingSerializer ()) {
177
+ unwrappingSerializer .serialize (value , gen , serializers );
178
+ }
179
+ }
180
+ }
181
+
182
+ private JsonSerializer <Object > getUnwrappingSerializer (Class <?> type ) throws JsonMappingException {
183
+ JsonSerializer <Object > serializer = cache .get (type );
184
+ if (serializer == null ) {
185
+ serializer = createUnwrappingSerializer (type );
186
+ cache .put (type , serializer );
187
+ }
188
+ return serializer ;
189
+ }
190
+
191
+ private JsonSerializer <Object > createUnwrappingSerializer (Class <?> type ) throws JsonMappingException {
192
+ JsonSerializer <Object > serializer = serializers .findValueSerializer (type );
193
+ return serializer .unwrappingSerializer (NameTransformer .NOP );
194
+ }
195
+ }
193
196
}
0 commit comments