# Observability - PHP SDK

> Explore the Temporal Developer’s guide on observability to learn about Visibility APIs and Search Attributes, helping you manage Workflow Executions efficiently.

The observability section of the Temporal Developer's guide covers the many ways to view the current state of your [Temporal Application](/temporal#temporal-application)—that is, ways to view which [Workflow Executions](/workflow-execution) are tracked by the [Temporal Platform](/temporal#temporal-platform) and the state of any specified Workflow Execution, either currently or at points of an execution.

This section covers features related to viewing the state of the application, including:

- [Log from a Workflow](#logging)
- [Visibility](#visibility)

## Log from a Workflow 

Logging enables you to record critical information during code execution.
Loggers create an audit trail and capture information about your Workflow's operation.
An appropriate logging level depends on your specific needs.
During development or troubleshooting, you might use debug or even trace.
In production, you might use info or warn to avoid excessive log volume.

The logger supports the following logging levels:

| Level   | Use                                                                                                       |
| ------- | --------------------------------------------------------------------------------------------------------- |
| `TRACE` | The most detailed level of logging, used for very fine-grained information.                               |
| `DEBUG` | Detailed information, typically useful for debugging purposes.                                            |
| `INFO`  | General information about the application's operation.                                                    |
| `WARN`  | Indicates potentially harmful situations or minor issues that don't prevent the application from working. |
| `ERROR` | Indicates error conditions that might still allow the application to continue running.                    |

The Temporal SDK core normally uses `WARN` as its default logging level.

To get a PSR-3 compatible logger in your Workflow code, use the [`Workflow::getLogger()`](https://php.temporal.io/classes/Temporal-Workflow.html#method_getLogger) method.

```php
use Temporal\Workflow;

#[Workflow\WorkflowInterface]
class MyWorkflow
{
    #[Workflow\WorkflowMethod]
    public function execute(string $param): \Generator
    {
        Workflow::getLogger()->info('Workflow started', ['parameter' => $param]);

        // Your workflow implementation

        Workflow::getLogger()->info('Workflow completed');
        return 'Done';
    }
}
```

The Workflow logger automatically enriches log context with the current Task Queue name.

Logs in replay mode are omitted unless the [`enableLoggingInReplay`](https://php.temporal.io/classes/Temporal-Worker-WorkerOptions.html#method_withEnableLoggingInReplay) Worker option is set to true.

```php
$factory = WorkerFactory::create();
$worker = $factory->newWorker('your-task-queue', WorkerOptions::new()
    ->withEnableLoggingInReplay(true)
);
```

### Default Logger

By default, PHP SDK uses a [`StderrLogger`](https://php.temporal.io/classes/Temporal-Worker-Logger-StderrLogger.html) that outputs log messages to the standard error stream.
These messages are automatically captured by RoadRunner and incorporated into its logging system with the INFO level, ensuring proper log collection in both development and production environments.
For more details on RoadRunner's logging capabilities, see the [RoadRunner Logger documentation](https://docs.roadrunner.dev/docs/logging-and-observability/logger).

### How to provide a custom logger 

You can set a custom PSR-3 compatible logger when creating a Worker:

```php
$myLogger = new MyLogger();

$workerFactory = WorkerFactory::create(converter: $converter);
$worker = $workerFactory->newWorker(
    taskQueue: 'my-task-queue',
    logger: $myLogger,
);
```

## Visibility APIs 

The term Visibility, within the Temporal Platform, refers to the subsystems and APIs that enable an operator to view Workflow Executions that currently exist within a Temporal Service.

### How to use Search Attributes 

The typical method of retrieving a Workflow Execution is by its Workflow Id.

However, sometimes you'll want to retrieve one or more Workflow Executions based on another property. For example, imagine you want to get all Workflow Executions of a certain type that have failed within a time range, so that you can start new ones with the same arguments.

You can do this with [Search Attributes](/search-attribute).

- [Default Search Attributes](/search-attribute#default-search-attribute) like `WorkflowType`, `StartTime` and `ExecutionStatus` are automatically added to Workflow Executions.
- [Custom Search Attributes](/search-attribute#custom-search-attribute) can contain their own domain-specific data (like `customerId` or `numItems`).

The steps to using custom Search Attributes are:

- Create a new Search Attribute in your Temporal Service using `temporal operator search-attribute create` or the Cloud UI.
- Set the value of the Search Attribute for a Workflow Execution:
  - On the Client by including it as an option when starting the Execution.
  - In the Workflow by calling `UpsertSearchAttributes`.
- Read the value of the Search Attribute:
  - On the Client by calling `DescribeWorkflow`.
  - In the Workflow by looking at `WorkflowInfo`.
- Query Workflow Executions by the Search Attribute using a [List Filter](/list-filter):
  - [In the Temporal CLI](/cli/command-reference/workflow#list).
  - In code by calling `ListWorkflowExecutions`.

Here is how to query Workflow Executions:

Use the [listWorkflowExecutions()](https://php.temporal.io/classes/Temporal-Client-WorkflowClientInterface.html#method_listWorkflowExecutions) method on the Client and pass a [List Filter](/list-filter) as an argument to filter the listed Workflows.
The result is an iterable paginator, so you can use the `foreach` loop to iterate over the results.

```php
$paginator = $workflowClient->listWorkflowExecutions('WorkflowType="GreetingWorkflow"');

foreach ($paginator as $info) {
    echo "Workflow ID: {$info->execution->getID()}\n";
}
```

### How to set custom Search Attributes 

After you've created custom Search Attributes in your Temporal Service (using `temporal operator search-attribute create` or the Cloud UI), you can set the values of the custom Search Attributes when starting a Workflow.

To set custom Search Attributes, use the `withTypedSearchAttributes` method on `WorkflowOptions` for a Workflow stub.
Typed search attributes are a `TypedSearchAttributes` collection.

```php
$keyDestinationTime = SearchAttributeKey::forDatetime('DestinationTime');
$keyOrderId = SearchAttributeKey::forKeyword('OrderId');

$workflow = $workflowClient->newWorkflowStub(
    OrderWorkflowInterface::class,
    WorkflowOptions::new()
        ->withWorkflowExecutionTimeout('10 minutes')
        ->withTypedSearchAttributes(
            TypedSearchAttributes::empty()
                ->withValue($keyOrderId, $orderid)
                ->withValue($keyDestinationTime, new \DateTimeImmutable('2028-11-05T00:10:07Z'))
        ),
);
```

### How to upsert Search Attributes 

Within the Workflow code, you can dynamically add or update Search Attributes using [`upsertTypedSearchAttributes`](https://php.temporal.io/classes/Temporal-Workflow.html#method_upsertTypedSearchAttributes).
This method is particularly useful for Workflows whose attributes need to change based on internal logic or external events.

```php
#[Workflow\UpdateMethod]
public function postponeDestinationTime(\DateInterval $interval)
{
    // Get the key for the DestinationTime attribute
    $keyDestinationTime = SearchAttributeKey::forDatetime('DestinationTime');

    /** @var DateTimeImmutable $destinationTime */
    $destinationTime = Workflow::getInfo()->typedSearchAttributes->get($keyDestinationTime);

    Workflow::upsertTypedSearchAttributes(
        $keyDestinationTime->valueSet($destinationTime->add($interval)),
    );
}
```

### How to remove a Search Attribute from a Workflow 

To remove a Search Attribute that was previously set, set it to an empty Map.

```php
#[Workflow\UpdateMethod]
public function unsetDestinationTime()
{
    // Get the key for the DestinationTime attribute
    $keyDestinationTime = SearchAttributeKey::forDatetime('DestinationTime');

    Workflow::upsertTypedSearchAttributes(
        $keyDestinationTime->valueUnset(),
    );
}
```
