How to handle workflow/activity logging with temporal Logger?

Hi, what is the correct way to handle logging now GetLogger(ctx) returns a simple Logger?
I am used to .WithField() from logrus, allowing me to build a logger with context.

Does anyone have an example with logrus (or zap)?

Within a workflow:

func MyWorkflow(ctx workflow.Context, req MyWorkflowPayload) error {
	logger := workflow.GetLogger(ctx)
	logger.Info("MyWorkflow")
	// ... do stuff
}

Within an activity:

func MyActivity(ctx context.Context, req MyActivityPayload) (MyActivityResult, error) {
	logger := activity.GetLogger(ctx)
	logger.Info("MyActivity")
	// ... do stuff
}

Setting up the worker:

import (
    "go.uber.org/zap"

    "go.temporal.io/sdk/client"
    "go.temporal.io/sdk/worker"
    "go.temporal.io/server/common/log"
)

// ...

logger, err := zap.NewDevelopment()

c, err := client.NewClient(client.Options{
    Logger: log.NewZapAdapter(logger),
})

w := worker.New(c, "task-queue", worker.Options{})

w.RegisterWorkflow(MyWorkflow)

w.RegisterActivity(MyActivity)

This works for us.

I reckon you could follow NewZapAdapter to create an adapter for other loggers, e.g. logrus. In our case, we just got lazy, and used what was available :slight_smile:

1 Like

@mrsaints: Hi! So every time you log, you include all keyvals of the whole workflow/activity?

@hazcod We don’t, we log what we deem necessary as the arguments to the workflow, and activity is already captured as part of the state.

EDIT: But, if you are asking if we can still use Zap fields, the answer is yes.

@mrsaints : hmm, what I would like to do at the start of my activity:

	info := activity.GetInfo(ctx)
	logger := activity.GetLogger(ctx).
		WithField("domain", input.Domain.Name).
		WithField("activity_type", info.ActivityType.Name).
		WithField("activity_id", info.ActivityID).
		WithField("workflow_runid", info.WorkflowExecution.RunID).
		WithField("attempt", info.Attempt).
		WithField("domain", input.Domain.Name)

	logger.Debug("started DiscoverDomainActivity")
         ... 
         logger.Info("something important happened")

How do you pass on all that context to every log call?

1 Like

Not sure if it’s exported, but there is a With function that allows you to do that: https://github.com/temporalio/go-sdk/blob/master/internal/log/with_logger.go#L32

Might need to be exported in the log package.

I believe most of these fields are already added by the SDK here.

Exactly what I need, but I suppose I cant use that since its not exported

You can just copy-paste the function. Both interfaces are exported, so you don’t have to copy those, but thanks to the implicit implementation (and no reference to unexported symbols), you could make this work, even if the interfaces weren’t exported.

You can also send a PR :wink:

Why do you need to copy that code if it already injected all the fields into the logger before returning it to you?

I guess injecting a bunch of values at the beginning of an activity is still a valid use case, regardless of the existing context.

1 Like

Indeed, it’s not so much workflow/activity metadata but things I add along the way.
(e.g. arguments)
Will using GetLogger(ctx) use the same log level as defined during client setup? I’m unsure how that works.