Skip to main content

Structured Logging and GCP Integration

Flyte uses a structured, context-aware logging system built on top of logrus. This system allows you to propagate execution metadata (like project, domain, and workflow IDs) through the call stack and ensures logs are formatted correctly for cloud-native environments like Google Cloud Platform (GCP).

Logging with Context

To ensure that logs include relevant metadata, use the context-aware logging functions provided by the github.com/flyteorg/flyte/v2/flytestdlib/logger package. These functions extract fields from the context.Context using contextutils.GetLogFields.

import (
"context"
"github.com/flyteorg/flyte/v2/flytestdlib/logger"
"github.com/flyteorg/flyte/v2/flytestdlib/contextutils"
)

func ProcessTask(ctx context.Context) {
// Add metadata to context
ctx = contextutils.WithProjectDomain(ctx, "my-project", "development")
ctx = contextutils.WithWorkflowID(ctx, "my-workflow-id")

// Log with context
logger.Infof(ctx, "Starting task processing")

if err := doWork(); err != nil {
logger.Errorf(ctx, "Task failed: %v", err)
}
}

The logger automatically extracts keys defined in flytestdlib/contextutils, such as project, domain, wf (Workflow ID), and exec_id.

Configuring the Global Logger

The logger is configured via the Logger configuration section. You can set these values in your Flyte configuration file or via environment variables.

Configuration Structure

The Config struct in flytestdlib/logger/config.go defines the available options:

type Config struct {
// Includes source code location (file:line) in logs.
IncludeSourceCode bool `json:"show-source" pflag:",Includes source code location in logs."`

// Mutes all logs regardless of severity.
Mute bool `json:"mute" pflag:",Mutes all logs regardless of severity."`

// Sets the minimum logging level (0: Panic, 1: Fatal, 2: Error, 3: Warn, 4: Info, 5: Debug).
Level Level `json:"level" pflag:",Sets the minimum logging level."`

Formatter FormatterConfig `json:"formatter" pflag:",Sets logging format."`
}

Setting Configuration in Code

You can programmatically update the logger configuration using logger.SetConfig:

import "github.com/flyteorg/flyte/v2/flytestdlib/logger"

func init() {
err := logger.SetConfig(&logger.Config{
Level: logger.DebugLevel,
Formatter: logger.FormatterConfig{
Type: logger.FormatterJSON,
},
IncludeSourceCode: true,
})
if err != nil {
panic(err)
}
}

GCP Stackdriver Integration

When deploying Flyte to GCP, you should use the gcp formatter. This formatter outputs logs in a JSON format compatible with GCP Cloud Logging (Stackdriver), mapping logrus levels to GCP-specific severity strings.

Enabling the GCP Formatter

Set the formatter type to gcp in your configuration:

Via YAML:

logger:
level: 4
formatter:
type: "gcp"

Via Environment Variables:

LOGGER_FORMATTER_TYPE=gcp
LOGGER_LEVEL=4

Severity Mapping

The GcpFormatter in flytestdlib/logger/gcp_formatter.go maps internal log levels to GCP severities as follows:

Flyte/Logrus LevelGCP Severity
DebugLevelDEBUG
InfoLevelINFO
WarnLevelWARNING
ErrorLevelERROR
FatalLevelCRITICAL
PanicLevelALERT

Advanced Logging Options

Including Source Code Location

Setting show-source: true (or IncludeSourceCode: true) adds a src field to every log entry containing the filename and line number.

Warning: This uses runtime.Caller(3) to identify the call site, which incurs a performance penalty. It is recommended primarily for development or debugging environments.

Muting Logs

The mute option completely disables the logger. This is primarily used in benchmarks or unit tests to prevent log noise from interfering with output.

cfg := &logger.Config{
Mute: true,
}
logger.SetConfig(cfg)

Log Levels

Flyte uses integer-based levels for configuration:

  • 0: Panic
  • 1: Fatal
  • 2: Error
  • 3: Warn (Default)
  • 4: Info
  • 5: Debug

You can check if a specific level is enabled before performing expensive log message construction using logger.IsLoggable(ctx, level):

if logger.IsLoggable(ctx, logger.DebugLevel) {
logger.Debugf(ctx, "Expensive data: %s", computeExpensiveString())
}