19
19
import static com .google .common .base .Preconditions .checkNotNull ;
20
20
21
21
import com .google .cloud .MonitoredResource ;
22
+ import com .google .cloud .logging .Payload .Type ;
22
23
import com .google .common .base .Function ;
23
24
import com .google .common .base .MoreObjects ;
24
25
import com .google .common .collect .ImmutableMap ;
26
+ import com .google .gson .Gson ;
27
+ import com .google .gson .GsonBuilder ;
28
+ import com .google .gson .JsonElement ;
29
+ import com .google .gson .JsonObject ;
30
+ import com .google .gson .JsonPrimitive ;
31
+ import com .google .gson .JsonSerializationContext ;
32
+ import com .google .gson .JsonSerializer ;
25
33
import com .google .logging .v2 .LogEntryOperation ;
26
34
import com .google .logging .v2 .LogEntrySourceLocation ;
27
35
import com .google .logging .v2 .LogName ;
@@ -61,8 +69,8 @@ public LogEntry apply(com.google.logging.v2.LogEntry pb) {
61
69
private final HttpRequest httpRequest ;
62
70
private final Map <String , String > labels ;
63
71
private final Operation operation ;
64
- private final Object trace ;
65
- private final Object spanId ;
72
+ private final String trace ;
73
+ private final String spanId ;
66
74
private final boolean traceSampled ;
67
75
private final SourceLocation sourceLocation ;
68
76
private final Payload <?> payload ;
@@ -80,8 +88,8 @@ public static class Builder {
80
88
private HttpRequest httpRequest ;
81
89
private Map <String , String > labels = new HashMap <>();
82
90
private Operation operation ;
83
- private Object trace ;
84
- private Object spanId ;
91
+ private String trace ;
92
+ private String spanId ;
85
93
private boolean traceSampled ;
86
94
private SourceLocation sourceLocation ;
87
95
private Payload <?> payload ;
@@ -245,7 +253,7 @@ public Builder setTrace(String trace) {
245
253
* relative resource name, the name is assumed to be relative to `//tracing.googleapis.com`.
246
254
*/
247
255
public Builder setTrace (Object trace ) {
248
- this .trace = trace ;
256
+ this .trace = trace != null ? trace . toString () : null ;
249
257
return this ;
250
258
}
251
259
@@ -257,7 +265,7 @@ public Builder setSpanId(String spanId) {
257
265
258
266
/** Sets the ID of the trace span associated with the log entry, if any. */
259
267
public Builder setSpanId (Object spanId ) {
260
- this .spanId = spanId ;
268
+ this .spanId = spanId != null ? spanId . toString () : null ;
261
269
return this ;
262
270
}
263
271
@@ -575,6 +583,142 @@ com.google.logging.v2.LogEntry toPb(String projectId) {
575
583
return builder .build ();
576
584
}
577
585
586
+ /**
587
+ * Customized serializers to match the expected format for timestamp, source location and request
588
+ * method
589
+ */
590
+ static final class InstantSerializer implements JsonSerializer <Instant > {
591
+ @ Override
592
+ public JsonElement serialize (
593
+ Instant src , java .lang .reflect .Type typeOfSrc , JsonSerializationContext context ) {
594
+ return new JsonPrimitive (src .toString ());
595
+ }
596
+ }
597
+
598
+ static final class SourceLocationSerializer implements JsonSerializer <SourceLocation > {
599
+ @ Override
600
+ public JsonElement serialize (
601
+ SourceLocation src , java .lang .reflect .Type typeOfSrc , JsonSerializationContext context ) {
602
+ JsonObject obj = new JsonObject ();
603
+ if (src .getFile () != null ) {
604
+ obj .addProperty ("file" , src .getFile ());
605
+ }
606
+ if (src .getLine () != null ) {
607
+ obj .addProperty ("line" , src .getLine ().toString ());
608
+ }
609
+ if (src .getFunction () != null ) {
610
+ obj .addProperty ("function" , src .getFunction ());
611
+ }
612
+ return obj ;
613
+ }
614
+ }
615
+
616
+ static final class RequestMethodSerializer implements JsonSerializer <HttpRequest .RequestMethod > {
617
+ @ Override
618
+ public JsonElement serialize (
619
+ HttpRequest .RequestMethod src ,
620
+ java .lang .reflect .Type typeOfSrc ,
621
+ JsonSerializationContext context ) {
622
+ return new JsonPrimitive (src .name ());
623
+ }
624
+ }
625
+
626
+ /** Helper class to format one line Json representation of the LogEntry for structured log. */
627
+ static final class StructuredLogFormatter {
628
+ private final Gson gson ;
629
+ private final StringBuilder builder ;
630
+
631
+ public StructuredLogFormatter (StringBuilder builder ) {
632
+ checkNotNull (builder );
633
+ this .gson =
634
+ new GsonBuilder ()
635
+ .registerTypeAdapter (Instant .class , new InstantSerializer ())
636
+ .registerTypeAdapter (SourceLocation .class , new SourceLocationSerializer ())
637
+ .registerTypeAdapter (HttpRequest .RequestMethod .class , new RequestMethodSerializer ())
638
+ .create ();
639
+ this .builder = builder ;
640
+ }
641
+
642
+ /**
643
+ * Adds a Json field and value pair to the current string representation. Method does not
644
+ * validate parameters to be multi-line strings. Nothing is added if {@code value} parameter is
645
+ * {@code null}.
646
+ *
647
+ * @param name a valid Json field name string.
648
+ * @param value an object to be serialized to Json using {@link Gson}.
649
+ * @param appendComma a flag to add a trailing comma.
650
+ * @return a reference to this object.
651
+ */
652
+ public StructuredLogFormatter appendField (String name , Object value , boolean appendComma ) {
653
+ checkNotNull (name );
654
+ if (value != null ) {
655
+ builder .append (gson .toJson (name )).append (":" ).append (gson .toJson (value ));
656
+ if (appendComma ) {
657
+ builder .append ("," );
658
+ }
659
+ }
660
+ return this ;
661
+ }
662
+
663
+ public StructuredLogFormatter appendField (String name , Object value ) {
664
+ return appendField (name , value , true );
665
+ }
666
+
667
+ /**
668
+ * Serializes a dictionary of key, values as Json fields.
669
+ *
670
+ * @param value a {@link Map} of key, value arguments to be serialized using {@link Gson}.
671
+ * @param appendComma a flag to add a trailing comma.
672
+ * @return a reference to this object.
673
+ */
674
+ public StructuredLogFormatter appendDict (Map <String , Object > value , boolean appendComma ) {
675
+ if (value != null ) {
676
+ String json = gson .toJson (value );
677
+ // append json object without brackets
678
+ if (json .length () > 1 ) {
679
+ builder .append (json .substring (0 , json .length () - 1 ).substring (1 ));
680
+ if (appendComma ) {
681
+ builder .append ("," );
682
+ }
683
+ }
684
+ }
685
+ return this ;
686
+ }
687
+ }
688
+
689
+ /**
690
+ * Serializes the object to a one line JSON string in the simplified format that can be parsed by
691
+ * the logging agents that run on Google Cloud resources.
692
+ */
693
+ public String toStructuredJsonString () {
694
+ if (payload .getType () == Type .PROTO ) {
695
+ throw new UnsupportedOperationException ("LogEntry with protobuf payload cannot be converted" );
696
+ }
697
+
698
+ final StringBuilder builder = new StringBuilder ("{" );
699
+ final StructuredLogFormatter formatter = new StructuredLogFormatter (builder );
700
+
701
+ formatter
702
+ .appendField ("severity" , severity )
703
+ .appendField ("timestamp" , timestamp )
704
+ .appendField ("httpRequest" , httpRequest )
705
+ .appendField ("logging.googleapis.com/insertId" , insertId )
706
+ .appendField ("logging.googleapis.com/labels" , labels )
707
+ .appendField ("logging.googleapis.com/operation" , operation )
708
+ .appendField ("logging.googleapis.com/sourceLocation" , sourceLocation )
709
+ .appendField ("logging.googleapis.com/spanId" , spanId )
710
+ .appendField ("logging.googleapis.com/trace" , trace )
711
+ .appendField ("logging.googleapis.com/trace_sampled" , traceSampled );
712
+ if (payload .getType () == Type .STRING ) {
713
+ formatter .appendField ("message" , payload .getData (), false );
714
+ } else if (payload .getType () == Type .JSON ) {
715
+ Payload .JsonPayload jsonPayload = (Payload .JsonPayload ) payload ;
716
+ formatter .appendDict (jsonPayload .getDataAsMap (), false );
717
+ }
718
+ builder .append ("}" );
719
+ return builder .toString ();
720
+ }
721
+
578
722
/** Returns a builder for {@code LogEntry} objects given the entry payload. */
579
723
public static Builder newBuilder (Payload <?> payload ) {
580
724
return new Builder (payload );
0 commit comments