# Logging in Inngest
Source: https://www.inngest.com/docs/guides/logging

Log handling can have some caveats when working with serverless runtimes.

One of the main problems is due to how serverless providers terminate after a function exits.
There might not be enough time for a logger to finish flushing, which results in logs being lost.

Another (opposite) problem is due to how Inngest handles memoization and code execution via HTTP calls to the SDK.
A log statement outside of `step` function could end up running multiple times, resulting in duplicated deliveries.

```ts {{ title: "example-fn.ts" }}
async ({ event, step }) => {
  console.log("something") // this can be run three times

  await step.run("fn", () => {
    console.log("something else") // this will always be run once
  })

  await step.run(...)
}
```

We provide a thin wrapper over existing logging tools, and export it to Inngest functions in order to mitigate these problems, so you, as the user, don't need to deal with them and things should work as you expect.

## Usage

A `logger` object is available within all Inngest functions as a handler argument. You can use it with the logger of your choice, or if absent, `logger` will default to use `console`.

```ts
inngest.createFunction(
  { id: "my-awesome-function" },
  { event: "func/awesome" },
  async ({ event, step, logger }) => { // <== logger is available as an argument
    logger.info("starting function", { metadataKey: "metadataValue" });

    await step.run("do-something", () => {
      if (somethingBadHappens) logger.warn("something bad happened");
    });

    return { success: true, event };
  }
);
```

<Tip>
We recommend using a logger library that supports a child logger `.child()` implementation which automatically adds function runtime metadata to your logs. Read more about [enriched logs with function metadata](#enriched-logs-with-function-metadata) for more details.
</Tip>

The exported logger provides the following interface methods:

```ts
export interface Logger {
  info(...args: any[]): void;
  warn(...args: any[]): void;
  error(...args: any[]): void;
  debug(...args: any[]): void;
}
```

These are very typical interfaces and are also on the [RFC5424 guidelines](https://datatracker.ietf.org/doc/html/rfc5424#section-6.2.1), so most loggers you choose should work without issues.

## Using your preferred logger

While `console.log` may be good enough for local development, other logging libraries provide more features that are suitable for production use.

The following examples use [winston][winston] for the logging library, including a basic example and an example with a Datadog transport.

<CodeGroup>
```ts {{ title: "Winston basic example" }}
winston.createLogger({
  level: "info",
  exitOnError: false,
  format: winston.format.json(),
  transports: [new winston.transports.Console()],
});

// Pass `logger` to the Inngest client, and this winston logger will be accessible within functions
inngest = new Inngest({
  id: "my-awesome-app",
  logger: logger,
  // ...
});
```
```ts {{ title: "Winston with Datadog transport" }}
/// Assuming we're deploying to Vercel.
/// Other providers likely have their own pre-defined environment variables you can use.
process.env.VERCEL_ENV || "development";
{
  host: "http-intake.logs.datadoghq.com",
  path: `/api/v2/logs?dd-api-key=${process.env.DD_API_KEY}&ddsource=nextjs&service=inngest&ddtags=env:${env}`,
  ssl: true,
};

winston.createLogger({
  level: "info",
  exitOnError: false,
  format: winston.format.json(),
  transports: [
    new winston.transports.Console(),
    new winston.transports.Http(ddTransportOps),
  ],
});

// Pass `logger` to the Inngest client, and this winston logger will be accessible within functions
inngest = new Inngest({
  id: "my-awesome-app",
  logger: logger,
  // ...
});
```
</CodeGroup>

## Enriched logs with function metadata

If the logger library supports a child logger `.child()` implementation, the built-in middleware will utilize it to add function runtime metadata to your logs automatically:

- Function name
- Event name
- Run ID

```ts {{ title: "Example usage with Winston logger" }}
await step.run("summarize-content", async ({ step, logger }) => {
  logger.info("calling Claude", { max_tokens: 1000 });
});
```
```json {{ title: "Example log output" }}
{"eventName":"inngest/function.invoked","functionName":"Summarize content via GPT-4",
"level":"info","max_tokens":1000,"message":"Querying vector database",
"runID":"01KB7YQXYNPEX3XB257A3RQDRX"}
```

## Loggers supported

The following is a list of loggers we're aware of that work, but is not an exhaustive list:

- [Winston][winston] <Tag>child logger support</Tag>
- [Pino](https://github.com/pinojs/pino) <Tag>child logger support</Tag>
- [Bunyan](https://github.com/trentm/node-bunyan) <Tag>child logger support</Tag>
- [Roarr](https://github.com/gajus/roarr) <Tag>child logger support</Tag>
- [LogLevel](https://github.com/pimterry/loglevel)
- [Log4js](https://github.com/log4js-node/log4js-node)
- [npmlog](https://github.com/npm/npmlog) (doesn't have `.debug()` but has a way to add custom levels)
- [Tracer](https://github.com/baryon/tracer)
- [Signale](https://github.com/klaudiosinani/signale)

## Customizing the logger

The built-in logger is implemented using [middleware](/docs/features/middleware). You can create your own middleware to customize the logger to your needs. See the [logging middleware example](/docs/reference/middleware/examples#logging) for more details.

[winston]: https://github.com/winstonjs/winston