googleapis / nodejs-logging-winston

Node.js client integration between Stackdriver Logging and Winston.
https://cloud.google.com/logging/
Apache License 2.0
105 stars 50 forks source link

Impossible to correctly initialize trace id for Cloud Logging entries #790

Closed evil-shrike closed 1 year ago

evil-shrike commented 1 year ago

It's still not clear how to pass a trace id for entries so in Cloud Logging they would be correlated to each other.

When I use @google-cloud/logging directly, I can see log entries got correct attributes, with trace

import {Log, Logging} from '@google-cloud/logging';
  const metadata: LogEntry = {
    severity: severity,
    labels: {
      component: component,
    },
    httpRequest: req,
    resource: {
      labels: {
        function_name: component,
      },
      type: 'cloud_function',
    },
  };
  const traceHeader = req.header('X-Cloud-Trace-Context');
  if (traceHeader && project) {
    const [trace] = traceHeader.split('/');
    metadata.trace = `projects/${project}/traces/${trace}`;
  }
  const entry = log.entry(
    metadata,
    aux ? Object.assign(aux || {}, {text: message}) : message
  );
  await log.write(entry);

image

Now I'm trying to log via winston. It's a pain. I can't understand how to initialize trace id for log entries. Here's how I created a logger:

export function createCloudLogger() {
  const cloudLogger = winston.createLogger({
    level: LOG_LEVEL,
    // it seems the format isn't needed, the same behavior without it
    format: format.combine(
      format.errors({ stack: true }),
      format((info) => {
        info.trace = process.env.TRACE_ID;
        return info;
      })(),
      format.json()
    ),
    defaultMeta: getDefaultMetadataForTracing(),
    transports: [
      new LoggingWinston({
        projectId: process.env.GCP_PROJECT,
        labels: {
          component: <string>process.env.LOG_COMPONENT,
        },
        logName: "gaarf",
        resource: {
          labels: {
            function_name: <string>process.env.K_SERVICE,
          },
          type: "cloud_function",
        },
        useMessageField: false,
        redirectToStdout: false,
      }),
    ],
  });
  return cloudLogger;
}

The problem is that the logger puts all metadata into the metadata field, including trace: image The metadata object itself isn't needed, I'd like jsut to have jsonPayload with all fields, but the main problem is that 'trace' is put there, where Cloud Logging doesn't recognize it.

The logger is created inside a library while the environment for Cloud Function initializes env vars for it (LOG_COMPONENT, K_SERVICE, GCP_PROJECT, TRACE_ID).

In the end I can't group log entries for one execution. And I don't see that LoggingWinston would have anything about trace in its options.

evil-shrike commented 1 year ago

What I did in the end.

Inside a library I'm using inside my Cloud Function I create a winston logger with LoggingWinston transport. And use LOGGING_TRACE_KEY key to assing a trace id that I expect in some my env var.

export function createCloudLogger() {
  const cloudLogger = winston.createLogger({
    level: LOG_LEVEL,
    format: format.combine(
      format((info) => {
        info.trace = process.env.TRACE_ID;
        info[LOGGING_TRACE_KEY] = process.env.TRACE_ID;
        return info;
      })(),
    ),
    defaultMeta: getDefaultMetadataForTracing(),
    transports: [
      new LoggingWinston({
        projectId: process.env.GCP_PROJECT,
        labels: {
          component: <string>process.env.LOG_COMPONENT,
        },
        logName: "mylog",
        resource: {
          labels: {
            function_name: <string>process.env.K_SERVICE,
          },
          type: "cloud_function",
        },
        useMessageField: false,
        redirectToStdout: false,
      }),
    ],
  });
  return cloudLogger;
}

in outer code in my Cloud Function, I initialize the mention env var as :

  const traceHeader = req.header('X-Cloud-Trace-Context');
  if (traceHeader && project) {
    const [trace] = traceHeader.split('/');
    metadata.trace = `projects/${project}/traces/${trace}`;
    process.env.TRACE_ID = metadata.trace;
  }