@@ -86,6 +86,8 @@ export interface StructuredJson {
8686 // Properties not supported by all agents (e.g. Cloud Run, Functions)
8787 logName ?: string ;
8888 resource ?: object ;
89+ // Properties to be stored in jsonPayload when running in serverless (e.g. Cloud Run , Functions)
90+ [ key : string ] : unknown ;
8991}
9092
9193export interface ToJsonOptions {
@@ -202,7 +204,7 @@ class Entry {
202204 toJSON ( options : ToJsonOptions = { } , projectId = '' ) {
203205 const entry : EntryJson = extend ( true , { } , this . metadata ) as { } as EntryJson ;
204206 // Format log message
205- if ( Object . prototype . toString . call ( this . data ) === '[object Object]' ) {
207+ if ( this . isObject ( this . data ) ) {
206208 entry . jsonPayload = objToStruct ( this . data , {
207209 removeCircular : ! ! options . removeCircular ,
208210 stringify : true ,
@@ -243,7 +245,7 @@ class Entry {
243245 * Serialize an entry to a standard format for any transports, e.g. agents.
244246 * Read more: https://cloud.google.com/logging/docs/structured-logging
245247 */
246- toStructuredJSON ( projectId = '' ) {
248+ toStructuredJSON ( projectId = '' , useMessageField = true ) {
247249 const meta = this . metadata ;
248250 // Mask out the keys that need to be renamed.
249251 /* eslint-disable @typescript-eslint/no-unused-vars */
@@ -260,7 +262,7 @@ class Entry {
260262 ...validKeys
261263 } = meta ;
262264 /* eslint-enable @typescript-eslint/no-unused-vars */
263- const entry : StructuredJson = extend ( true , { } , validKeys ) as { } ;
265+ let entry : StructuredJson = extend ( true , { } , validKeys ) as { } ;
264266 // Re-map keys names.
265267 entry [ LABELS_KEY ] = meta . labels
266268 ? Object . assign ( { } , meta . labels )
@@ -273,9 +275,29 @@ class Entry {
273275 ? meta . traceSampled
274276 : undefined ;
275277 // Format log payload.
276- entry . message =
277- meta . textPayload || meta . jsonPayload || meta . protoPayload || undefined ;
278- entry . message = this . data || entry . message ;
278+ const data =
279+ this . data ||
280+ meta . textPayload ||
281+ meta . jsonPayload ||
282+ meta . protoPayload ||
283+ undefined ;
284+ if ( useMessageField ) {
285+ /** If useMessageField is set, we add the payload to {@link StructuredJson#message} field.*/
286+ entry . message = data ;
287+ } else {
288+ /** useMessageField is false, we add the structured payload to {@link StructuredJson} key-value map.
289+ * It could be especially useful for serverless environments like Cloud Run/Functions when stdout transport is used.
290+ * Note that text still added to {@link StructuredJson#message} field for text payload since it does not have fields within. */
291+ if ( data !== undefined && data !== null ) {
292+ if ( this . isObject ( data ) ) {
293+ entry = extend ( true , { } , entry , data ) ;
294+ } else if ( typeof data === 'string' ) {
295+ entry . message = data ;
296+ } else {
297+ entry . message = JSON . stringify ( data ) ;
298+ }
299+ }
300+ }
279301 // Format timestamp
280302 if ( meta . timestamp instanceof Date ) {
281303 entry . timestamp = meta . timestamp . toISOString ( ) ;
@@ -333,6 +355,15 @@ class Entry {
333355 }
334356 return serializedEntry ;
335357 }
358+
359+ /**
360+ * Determines whether `value` is a JavaScript object.
361+ * @param value The value to be checked
362+ * @returns true if `value` is a JavaScript object, false otherwise
363+ */
364+ private isObject ( value : unknown ) : value is object {
365+ return Object . prototype . toString . call ( value ) === '[object Object]' ;
366+ }
336367}
337368
338369/**
0 commit comments