# Logging

Every backend SDK emits structured logs so you can trace requests, debug failures, and feed the events into your observability stack. Logging is opt-in: attach a logger when you build the client and the SDK emits a small, fixed set of events with the same field names across every language.

## Enabling logging

Pass a logger when you construct the client. The SDK writes to it directly and never changes its level, so you control verbosity and destination through your own logging setup. Request and response events are emitted at `DEBUG`, the initialization event at `INFO`, and transport failures at `ERROR`, so set your logger to `DEBUG` to see per-request activity.

<Tabs>

```python label="Python"
import logging
from getstream import Stream

logging.basicConfig(level=logging.DEBUG)  # request/response events are emitted at DEBUG

client = Stream(
    api_key="your_api_key",
    api_secret="your_api_secret",
    logger=logging.getLogger("myapp.stream"),
    log_bodies=False,  # opt-in; set True to also log redacted request/response bodies
)
```

```ruby label="Ruby"
require 'logger'
require 'getstream_ruby'

client = GetStreamRuby::Client.new(
  api_key: 'your_api_key',
  api_secret: 'your_api_secret',
  logger: Logger.new($stdout),
  log_bodies: false # opt-in; set true to also log redacted request/response bodies
)
```

```php label="PHP"
use GetStream\ClientBuilder;

$client = (new ClientBuilder())
    ->apiKey('your_api_key')
    ->apiSecret('your_api_secret')
    ->logger($psr3Logger)  // any Psr\Log\LoggerInterface
    ->logBodies(false)     // opt-in; set true to also log redacted request/response bodies
    ->build();
```

```go label="Go"
import (
    "log"
    "os"

    stream "github.com/GetStream/getstream-go/v4"
)

logger := stream.NewDefaultLogger(os.Stderr, "", log.LstdFlags, stream.LogLevelDebug)

client, err := stream.NewClient("your_api_key", "your_api_secret",
    stream.WithLogger(logger),
    stream.WithLogBodies(false), // opt-in; set true to also log redacted request/response bodies
)
```

```csharp label="C#"
using GetStream;
using Microsoft.Extensions.Logging;

using var loggerFactory = LoggerFactory.Create(b => b
    .SetMinimumLevel(LogLevel.Debug)   // request/response events are emitted at Debug
    .AddConsole());

var client = new StreamClient(new StreamOptions
{
    ApiKey = "your_api_key",
    ApiSecret = "your_api_secret",
    Logger = loggerFactory.CreateLogger("GetStream"),
    LogBodies = false,   // opt-in; set true to also log redacted request/response bodies
});
```

```java label="Java"
import io.getstream.services.framework.StreamSDKClient;
import io.getstream.services.framework.StreamClientOptions;
import org.slf4j.LoggerFactory;

StreamClientOptions options = new StreamClientOptions()
    .setLogger(LoggerFactory.getLogger("io.getstream"))
    .setLogBodies(false); // opt-in; set true to also log redacted request/response bodies

StreamSDKClient client = new StreamSDKClient("your_api_key", "your_api_secret", options);
```

</Tabs>

## Log events

The SDK emits four events, identical across languages. Each carries the same set of fields regardless of the SDK you use.

| Event                    | Level         | When                                                                                            |
| ------------------------ | ------------- | ----------------------------------------------------------------------------------------------- |
| `client.initialized`     | INFO          | Once, when the client is constructed.                                                           |
| `http.request.sent`      | DEBUG         | Before each HTTP request.                                                                       |
| `http.response.received` | DEBUG         | After each response, including `4xx` and `5xx`.                                                 |
| `http.request.failed`    | ERROR / DEBUG | ERROR on a transport failure with no HTTP response; DEBUG for each attempt about to be retried. |

## Fields

The `client.initialized` event describes the client: `stream.sdk.name` and `stream.sdk.version`, plus the effective configuration under `stream.client.*` (connection-pool and timeout settings, and whether body logging is on).

The `http.*` events use OpenTelemetry-style keys: `http.request.method`, `url.path`, `url.query`, `http.response.status_code`, `http.response.body.size`, and `duration_ms`. On `http.request.failed`, `error.message` is always present and `error.type` is set for transport failures only (one of `connection_reset`, `timeout`, `dns_failure`, `tls_handshake_failed`, `unknown`). When an attempt is retried, `retry.attempt` records the attempt number.

## Redaction

Redaction is always on and cannot be disabled. The SDK never logs header values, so your API secret and auth tokens never appear in logs. It also replaces sensitive query parameters (`api_key`, `api_secret`, `token`) and known-secret body fields (`api_secret`, `token`, `password`) with `<redacted>`.

## Logging request and response bodies

Request and response bodies are not logged by default. You can opt in with the body-logging flag shown above for deep debugging. Bodies stay key-redacted, but they can still contain message content or user PII, so keep this disabled in production. Enabling it logs a one-time warning at construction as a reminder.


---

This page was last updated at 2026-07-24T16:13:19.864Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/dotnet-csharp/logging/](https://getstream.io/chat/docs/dotnet-csharp/logging/).