11package com .genexus .diagnostics .core .provider ;
22
3+ import com .genexus .diagnostics .LogLevel ;
34import com .genexus .diagnostics .core .ILogger ;
5+ import com .genexus .GxUserType ;
6+ import com .google .gson .*;
7+ import com .google .gson .reflect .TypeToken ;
8+ import org .apache .logging .log4j .Level ;
9+ import org .apache .logging .log4j .LogManager ;
10+ import org .apache .logging .log4j .ThreadContext ;
11+ import org .apache .logging .log4j .core .Appender ;
12+ import org .apache .logging .log4j .core .LoggerContext ;
13+ import org .apache .logging .log4j .core .appender .AbstractAppender ;
14+ import org .apache .logging .log4j .core .config .Configuration ;
15+ import org .apache .logging .log4j .layout .template .json .JsonTemplateLayout ;
16+ import org .apache .logging .log4j .message .ObjectMessage ;
17+
18+ import java .lang .reflect .Type ;
19+ import java .util .*;
420
521public class Log4J2Logger implements ILogger {
622 private org .apache .logging .log4j .Logger log ;
@@ -37,7 +53,7 @@ public void fatal(Throwable ex, String[] list) {
3753 }
3854
3955 public void fatal (String [] list ) {
40- fatal (null , list );
56+ fatal (( Throwable ) null , list );
4157 }
4258
4359 public void error (String msg , Throwable ex ) {
@@ -59,7 +75,7 @@ public void error(Throwable ex, String[] list) {
5975 }
6076
6177 public void error (String [] list ) {
62- error (null , list );
78+ error (( Throwable ) null , list );
6379 }
6480
6581 public void error (String msg ) {
@@ -80,7 +96,7 @@ public void warn(Throwable ex, String[] list) {
8096 }
8197
8298 public void warn (String [] list ) {
83- warn (null , list );
99+ warn (( Throwable ) null , list );
84100 }
85101
86102 public void warn (String msg , Throwable ex ) {
@@ -106,7 +122,7 @@ public void debug(Throwable ex, String[] list) {
106122 }
107123
108124 public void debug (String [] list ) {
109- debug (null , list );
125+ debug (( Throwable ) null , list );
110126 }
111127
112128 // Lambda Functions not supported JAVA 7. Only Java 8.
@@ -162,7 +178,7 @@ public void trace(Throwable ex, String[] list) {
162178 }
163179
164180 public void trace (String [] list ) {
165- trace (null , list );
181+ trace (( Throwable ) null , list );
166182 }
167183
168184 // Lambda Functions not supported JAVA 7. Only Java 8.
@@ -190,4 +206,204 @@ public boolean isErrorEnabled() {
190206 return log .isErrorEnabled ();
191207 }
192208
209+
210+
211+
212+
213+
214+
215+ /******** NEW methods to improve logging ********/
216+
217+ public void setContext (String key , Object value ) {
218+ // Add entry to the MDC (only works for JSON log format)
219+ ThreadContext .put (key , fromObjectToString (value ));
220+ }
221+
222+ private ObjectMessage buildLogMessage (String messageKey , Object messageValue , boolean stackTrace ) {
223+ Map <String , Object > messageMap ;
224+ String stacktraceLabel = "stackTrace" ;
225+ String msgLabel = "message" ;
226+
227+ if (isNullOrBlank (messageValue )) {
228+ if (stackTrace ) {
229+ messageMap = new LinkedHashMap <>();
230+ messageMap .put (msgLabel , messageKey );
231+ messageMap .put (stacktraceLabel , getStackTraceAsList ());
232+ if (isJsonLogFormat ())
233+ return new ObjectMessage (messageMap );
234+ else
235+ return new ObjectMessage (new Gson ().toJson (messageMap ));
236+ }
237+ return new ObjectMessage (messageKey );
238+ } else {
239+ messageMap = objectToMap (messageKey , messageValue );
240+ if (stackTrace ) {
241+ messageMap .put (stacktraceLabel , getStackTraceAsList ());
242+ }
243+ if (isJsonLogFormat ())
244+ return new ObjectMessage (messageMap );
245+ else
246+ return new ObjectMessage (new Gson ().toJson (messageMap ));
247+ }
248+ }
249+
250+ public void write (String message , int logLevel , Object data , boolean stackTrace ) {
251+ printLog ("data" , data , stackTrace , Level .DEBUG );
252+ }
253+
254+ private void printLog (final String messageKey , final Object messageValue , final boolean stackTrace ,
255+ final Level logLevel ) {
256+
257+ /* Generate the message JSON in this format:
258+ * { "message" :
259+ * {
260+ * "messageKey": "USER messageValue",
261+ * }
262+ * }
263+ * */
264+ ObjectMessage om = buildLogMessage (messageKey , messageValue , stackTrace );
265+
266+ // Log the message received or the crafted msg
267+ if (logLevel .equals (Level .FATAL )) log .fatal (om );
268+ else if (logLevel .equals (Level .ERROR )) log .error (om );
269+ else if (logLevel .equals (Level .WARN )) log .warn (om );
270+ else if (logLevel .equals (Level .INFO )) log .info (om );
271+ else if (logLevel .equals (Level .DEBUG )) log .debug (om );
272+ else if (logLevel .equals (Level .TRACE )) log .trace (om );
273+ }
274+
275+
276+
277+ private static String fromObjectToString (Object value ) {
278+ String res = "" ;
279+ if (value == null ) {
280+ res = "null" ;
281+ } else if (value instanceof String && isJson ((String ) value )) {
282+ // Avoid double serialization
283+ res = (String ) value ;
284+ } else if (value instanceof String ) {
285+ res = (String ) value ;
286+ } else if (value instanceof Number || value instanceof Boolean ) {
287+ res = value .toString ();
288+ } else if (value instanceof Map || value instanceof List ) {
289+ res = new Gson ().toJson (value );
290+ } else if (value instanceof GxUserType ) {
291+ res = ((GxUserType ) value ).toJSonString ();
292+ } else {
293+ // Any other object → serialize as JSON
294+ res = new Gson ().toJson (value );
295+ }
296+ return res ;
297+ }
298+
299+ private static boolean isJson (String input ) {
300+ try {
301+ JsonElement json = JsonParser .parseString (input );
302+ return json .isJsonObject () || json .isJsonArray ();
303+ } catch (Exception e ) {
304+ return false ;
305+ }
306+ }
307+
308+ private Map <String , Object > objectToMap (String key , Object value ) {
309+ Map <String , Object > result = new LinkedHashMap <>();
310+ if (value == null ) {
311+ result .put (key , null );
312+ } else if (value instanceof Number || value instanceof Boolean
313+ || value instanceof Map || value instanceof List ) {
314+ result .put (key , value );
315+ } else if (value instanceof GxUserType ) {
316+ result .put (key , jsonStringToMap (((GxUserType ) value ).toJSonString ()));
317+ } else if (value instanceof String ) {
318+ String str = (String ) value ;
319+
320+ // Try to parse as JSON
321+ try {
322+ JsonElement parsed = JsonParser .parseString (str );
323+ Gson gson = new Gson ();
324+ if (parsed .isJsonObject ()) {
325+ result .put (key , gson .fromJson (parsed , Map .class ));
326+ } else if (parsed .isJsonArray ()) {
327+ result .put (key , gson .fromJson (parsed , List .class ));
328+ } else if (parsed .isJsonPrimitive ()) {
329+ JsonPrimitive primitive = parsed .getAsJsonPrimitive ();
330+ if (primitive .isBoolean ()) {
331+ result .put (key , primitive .getAsBoolean ());
332+ } else if (primitive .isNumber ()) {
333+ result .put (key , primitive .getAsNumber ());
334+ } else if (primitive .isString ()) {
335+ result .put (key , primitive .getAsString ());
336+ }
337+ }
338+ } catch (JsonSyntaxException e ) {
339+ // Invalid JSON: it is left as string
340+ result .put (key , str );
341+ }
342+ } else {
343+ // Any other object: convert to string
344+ result .put (key , value .toString ());
345+ }
346+ return result ;
347+ }
348+
349+ private static String getStackTrace () {
350+ StringBuilder stackTrace ;
351+ stackTrace = new StringBuilder ();
352+ for (StackTraceElement ste : Thread .currentThread ().getStackTrace ()) {
353+ stackTrace .append (ste ).append (System .lineSeparator ());
354+ }
355+ return stackTrace .toString ();
356+ }
357+
358+ private static List <String > getStackTraceAsList () {
359+ List <String > stackTraceLines = new ArrayList <>();
360+ for (StackTraceElement ste : Thread .currentThread ().getStackTrace ()) {
361+ stackTraceLines .add (ste .toString ());
362+ }
363+ return stackTraceLines ;
364+ }
365+
366+ private static String stackTraceListToString (List <String > stackTraceLines ) {
367+ return String .join (System .lineSeparator (), stackTraceLines );
368+ }
369+
370+ // Convert a JSON String to Map<String, Object>
371+ private static Map <String , Object > jsonStringToMap (String jsonString ) {
372+ Gson gson = new Gson ();
373+ Type type = new TypeToken <Map <String , Object >>(){}.getType ();
374+ return gson .fromJson (jsonString , type );
375+ }
376+
377+ private String toJson (String key , Object value ) {
378+ Map <String , Object > map = new HashMap <>();
379+ map .put (key , value );
380+ return new Gson ().toJson (map );
381+ }
382+
383+ private static boolean isJsonLogFormat () {
384+ LoggerContext context = (LoggerContext ) LogManager .getContext (false );
385+ Configuration config = context .getConfiguration ();
386+
387+ for (Appender appender : config .getAppenders ().values ()) {
388+ if (appender instanceof AbstractAppender ) {
389+ Object layout = ((AbstractAppender ) appender ).getLayout ();
390+ if (layout instanceof JsonTemplateLayout ) {
391+ return true ;
392+ }
393+ }
394+ }
395+
396+ return false ;
397+ }
398+
399+ public static boolean isNullOrBlank (Object obj ) {
400+ if (obj == null ) {
401+ return true ;
402+ }
403+ if (obj instanceof String ) {
404+ return ((String ) obj ).trim ().isEmpty ();
405+ }
406+ return false ; // It is not null, and it isn't an empty string
407+ }
408+
193409}
0 commit comments