# Logging (/docs/template/logging)

<!-- agent-signals: reading_time_min: 3 · est_tokens: 1552 · updated: 2026-07-30 -->
Related: [Quickstart](/docs/template/quickstart.md), [How it works](/docs/template/how-it-works.md), [User and workdir](/docs/template/user-and-workdir.md), [Caching](/docs/template/caching.md), [Base image](/docs/template/base-image.md), [Private registries](/docs/template/private-registries.md)

You can retrieve the build logs using the SDK.

## Default logger [#default-logger]

We provide a default logger that you can use to filter logs by level:

<CodeGroup>
  <CodeBlockTabs defaultValue="JavaScript & TypeScript" groupId="javascript-typescript+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="JavaScript & TypeScript">
        JavaScript & TypeScript
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="JavaScript & TypeScript">
      ```typescript  
      import { Template, defaultBuildLogger } from 'e2b';

      await Template.build(template, 'my-template', {
        onBuildLogs: defaultBuildLogger({
          minLevel: "info", // Minimum log level to show
        }),
      });
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python">
      ```python  
      from e2b import Template, default_build_logger

      Template.build(
          template,
          'my-template',
          on_build_logs=default_build_logger(
              min_level="info",  # Minimum log level to show
          )
      )
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

## Custom logger [#custom-logger]

You can customize how logs are handled:

<CodeGroup>
  <CodeBlockTabs defaultValue="JavaScript & TypeScript" groupId="javascript-typescript+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="JavaScript & TypeScript">
        JavaScript & TypeScript
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="JavaScript & TypeScript">
      ```typescript  
      // Simple logging
      onBuildLogs: (logEntry) => console.log(logEntry.toString());

      // Custom formatting
      onBuildLogs: (logEntry) => {
        const time = logEntry.timestamp.toISOString();
        console.log(`[${time}] ${logEntry.level.toUpperCase()}: ${logEntry.message}`);
      };

      // Filter by log level
      onBuildLogs: (logEntry) => {
        if (logEntry.level === "error" || logEntry.level === "warn") {
          console.error(logEntry.toString());
        }
      };
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python">
      ```python  
      # Simple logging
      on_build_logs=lambda log_entry: print(log_entry)

      # Custom formatting
      def custom_logger(log_entry):
          time = log_entry.timestamp.isoformat()
          print(f"[{time}] {log_entry.level.upper()}: {log_entry.message}")

      Template.build(template, 'my-template', on_build_logs=custom_logger)

      # Filter by log level
      def error_logger(log_entry):
          if log_entry.level in ["error", "warn"]:
              print(f"ERROR/WARNING: {log_entry}", file=sys.stderr)

      Template.build(template, 'my-template', on_build_logs=error_logger)
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

The `onBuildLogs`/`on_build_logs` callback receives structured `LogEntry` objects with the following properties:

<CodeGroup>
  <CodeBlockTabs defaultValue="JavaScript & TypeScript" groupId="javascript-typescript+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="JavaScript & TypeScript">
        JavaScript & TypeScript
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="JavaScript & TypeScript">
      ```typescript  
      type LogEntryLevel = 'debug' | 'info' | 'warn' | 'error'

      class LogEntry {
        constructor(
          public readonly timestamp: Date,
          public readonly level: LogEntryLevel,
          public readonly message: string
        )

        toString() // Returns formatted log string
      }

      // Indicates the start of the build process
      class LogEntryStart extends LogEntry {
        constructor(timestamp: Date, message: string) {
          super(timestamp, 'debug', message)
        }
      }

      // Indicates the end of the build process
      class LogEntryEnd extends LogEntry {
        constructor(timestamp: Date, message: string) {
          super(timestamp, 'debug', message)
        }
      }
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python">
      ```python  
      LogEntryLevel = Literal["debug", "info", "warn", "error"]

      @dataclass
      class LogEntry:
          timestamp: datetime
          level: LogEntryLevel
          message: str

          def __str__(self) -> str:  # Returns formatted log string


      # Indicates the start of the build process
      @dataclass
      class LogEntryStart(LogEntry):
          level: LogEntryLevel = field(default="debug", init=False)

      # Indicates the end of the build process
      @dataclass
      class LogEntryEnd(LogEntry):
          level: LogEntryLevel = field(default="debug", init=False)
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

Together with the `LogEntry` type, there are also `LogEntryStart` and `LogEntryEnd` types that indicate the start and end of the build process. Their default log level is `debug` and you can use them to like this:

<CodeGroup>
  <CodeBlockTabs defaultValue="JavaScript & TypeScript" groupId="javascript-typescript+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="JavaScript & TypeScript">
        JavaScript & TypeScript
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="JavaScript & TypeScript">
      ```typescript  
      if (logEntry instanceof LogEntryStart) {
        // Build started
        return
      }

      if (logEntry instanceof LogEntryEnd) {
        // Build ended
        return
      }
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python">
      ```python  
      if isinstance(log_entry, LogEntryStart):
          # Build started
          return

      if isinstance(log_entry, LogEntryEnd):
          # Build ended
          return

      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>
