[Yandex Cloud documentation](../../../index.md) > [Yandex Cloud Functions](../../index.md) > [Step-by-step guides](../index.md) > Managing a function > Managing logs > Writing logs

# Writing to the function execution log

{% note info %}

Logging is not free of charge. For more information, see [Yandex Cloud Logging pricing](../../../logging/pricing.md).

{% endnote %}

{% list tabs group=instructions %}

- Management console {#console}
    
    1. In the [management console](https://console.yandex.cloud), navigate to the [folder](../../../resource-manager/concepts/resources-hierarchy.md#folder) containing the function.
    1. Navigate to **Cloud Functions**.
    1. Select the function you want to configure logging for.
    1. Navigate to the **Editor** tab.
    1. Under **Logging**:
        
        1. Enable **Write logs**.
        1. In the **Destination** field, select:
          
           * `Folder`: To write [logs](../../concepts/logs.md) to the default [log group](../../../logging/concepts/log-group.md) for the folder containing the function.
           * `Log group`: To write logs to a custom log group.
             
             Select the log group to write logs to or [create](../../../logging/operations/create-group.md) a new one.

        1. Optionally, select the minimum logging level.

    1. Click **Save changes**.
    
    If you set the minimum logging level, logs of that level and higher will be written to the execution log. With no minimum logging level set, all function logs will be written to the execution log.

- CLI {#cli}

    If you do not have the Yandex Cloud CLI yet, [install and initialize it](../../../cli/quickstart.md#install).

    The folder used by default is the one specified when [creating](../../../cli/operations/profile/profile-create.md) the CLI profile. To change the default folder, use the `yc config set folder-id <folder_ID>` command. You can also specify a different folder for any command using `--folder-name` or `--folder-id`. If you access a resource by its name, the search will be limited to the default folder. If you access a resource by its ID, the search will be global, i.e., through all folders based on access permissions.

    ### Logging destination {#destination}

    If no custom [log group](../../../logging/concepts/log-group.md) is specified in the function version settings or logging remains enabled, the function automatically writes all [logs](../../concepts/logs.md) to the default log group of the [folder](../../../resource-manager/concepts/resources-hierarchy.md#folder) it resides in.

    To write logs to another folder's default log group, provide that folder's ID in the `--log-folder-id` parameter when [creating a function version](version-manage.md). The [account](../../../iam/concepts/users/accounts.md) used to run the command must have the `logging.editor` [role](../../../logging/security/index.md#logging-editor) or higher for the folder.

    To write logs a custom log group, provide that log group's ID in the `--log-group-id` parameter when creating a function version. The log group may reside in a different folder. The account used to run the command must have the `logging.editor` role or higher for that folder.


    ### Minimum logging level {#log-level}

    To set the minimum logging level, specify it in the `--min-log-level` parameter when creating a function version. 
    
    If you set the minimum logging level, logs of that level and higher will be written to the execution log. With no minimum logging level set, all function logs will be written to the execution log.

    ### Disabling logging {#disabled}

    To disable logging, specify `--no-logging` when creating a function version.

    ### Command example {#example}
    
    To write logs to a custom log group, run this command:
        
    ```
    yc serverless function version create \
      --function-id <function_ID> \
      --runtime <runtime> \
      --entrypoint <entry_point> \
      --memory <RAM_amount> \
      --source-path <ZIP_archive_with_function_code> \
      --log-group-id <log_group_ID> \
      --min-log-level <minimum_logging_level>
    ```

    Where:
    * `--function-id`: Function ID.
    * `--runtime`: Runtime.
    * `entrypoint`: Entry point in `<file_name_without_extension>.<handler_name>` format.
    * `--memory`: Amount of RAM.
    * `--source-path`: ZIP archive with the function code and required dependencies.
    * `--log-group-id`: ID of the log group to write logs to.
    * `--min-log-level`: Minimum logging level. This is an optional setting.

    Result:
    ```
    done (4s)
    id: d4ech7qdki6r********
    function_id: d4e7tbg7m4np********
    created_at: "2024-04-19T10:13:00.019Z"
    runtime: python37
    entrypoint: index.handler
    resources:
      memory: "134217728"
    execution_timeout: 5s
    image_size: "53248"
    status: ACTIVE
    tags:
      - $latest
    log_options:
      log_group_id: e23u2vn449av********
      min_level: DEBUG
    ```

- Terraform {#tf}
    
    With [Terraform](https://www.terraform.io/), you can quickly create a cloud infrastructure in Yandex Cloud and manage it using configuration files. These files store the infrastructure description written in HashiCorp Configuration Language (HCL). If you change the configuration files, Terraform automatically detects which part of your configuration is already deployed, and what should be added or removed.
    
    Terraform is distributed under the [Business Source License](https://github.com/hashicorp/terraform/blob/main/LICENSE). The [Yandex Cloud provider for Terraform](https://github.com/yandex-cloud/terraform-provider-yandex) is distributed under the [MPL-2.0](https://www.mozilla.org/en-US/MPL/2.0/) license.
    
    For more information about the provider resources, see the guides on the [Terraform](https://www.terraform.io/docs/providers/yandex/index.html) website or [its mirror](../../../terraform/index.md).
    
    If you do not have Terraform yet, [install it and configure the Yandex Cloud provider](../../../tutorials/infrastructure-management/terraform-quickstart.md#install-terraform).
    
    
    To manage infrastructure using Terraform under a service account or user accounts (a Yandex account, a federated account, or a local user), [authenticate](../../../terraform/authentication.md) using the appropriate method.

    ### Logging destination {#destination}

    If no custom [log group](../../../logging/concepts/log-group.md) is specified in the function version settings or logging remains enabled, the function automatically writes all [logs](../../concepts/logs.md) to the default log group of the [folder](../../../resource-manager/concepts/resources-hierarchy.md#folder) it resides in.

    To write logs to another folder's default log group, provide that folder's ID under `log_options` in the `folder_id` parameter when [creating a function version](version-manage.md). The [account](../../../iam/concepts/users/accounts.md) used to run the command must have the `logging.editor` [role](../../../logging/security/index.md#logging-editor) or higher for the folder.

    To write logs to a custom log group, provide that log group's ID under `log_options` in the `log_group_id` parameter when creating a function version. The log group may reside in a different folder. The account used to run the command must have the `logging.editor` role or higher for that folder.

    ### Minimum logging level {#log-level}

    To set a minimum logging level, provide it under `log_options` in the `min_level` parameter when creating a function version.

    If you set the minimum logging level, logs of that level and higher will be written to the execution log. With no minimum logging level set, all function logs will be written to the execution log.

    ### Disabling logging {#disabled}

    To disable logging, specify `disabled` set to `true` under `log_options` when creating a function version.

    ### Example {#example}

    To write logs to a custom log group:

    1. Open the Terraform configuration file and add the `log_options` section to the `yandex_function` resource description:

        Here are some examples of the configuration file structure:

        ```hcl
        resource "yandex_function" "my-function" {
          name       = "<function_name>"
          user_hash  = "<function_version_hash>"
          runtime    = "<runtime_environment>"
          entrypoint = "<entry_point>"
          memory     = "<RAM_amount>"
          content {
            zip_filename = "<path_to_ZIP_archive>"
          }
          log_options {
            log_group_id = "<log_group_ID>"
            min_level    = "<minimum_logging_level>"
          }
        }
        ```

        Where:
        * `name`: Function name.
        * `user_hash`: Any string to identify the function version. When you change the function, update this string as well. Updating this string triggers a function update.
        * `runtime`: Function [runtime](../../concepts/runtime/index.md).
        * `entrypoint`: Function name in the source code that will serve as an entry point to applications.
        * `memory`: Amount of memory allocated for the function, in MB.
        * `content`: Function source code:
            * `zip_filename`: Path to the ZIP archive with the function source code and required dependencies.
        * `log_options`: Logging settings:
            * `log_group_id`: ID of the log group to write logs to.
            * `min_level`: Minimum logging level. This is an optional setting.

        For more on the properties of the `yandex_function` resource, see [this provider guide](../../../terraform/resources/function.md).

    1. Apply the changes:

        1. In the terminal, navigate to the configuration file directory.
        1. Make sure the configuration is correct using this command:
        
           ```bash
           terraform validate
           ```
        
           If the configuration is valid, you will get this message:
        
           ```bash
           Success! The configuration is valid.
           ```
        
        1. Run this command:
        
           ```bash
           terraform plan
           ```
        
           You will see a list of resources and their properties. No changes will be made at this step. Terraform will show any errors in the configuration.
        1. Apply the configuration changes:
        
           ```bash
           terraform apply
           ```
        
        1. Type `yes` and press **Enter** to confirm the changes.

- API {#api}

    To write logs to the function execution log, use the [createVersion](../../functions/api-ref/Function/createVersion.md) REST API method for the [Function](../../functions/api-ref/Function/index.md) resource or the [FunctionService/LogOptions](../../functions/api-ref/grpc/Function/createVersion.md#yandex.cloud.serverless.functions.v1.LogOptions) gRPC API call.

{% endlist %}

## Structured logs {#structured-logs}

Apart from text, you can write [structured logs](../../concepts/logs.md#structured-logs) to the standard output (`stdout`) and standard error output (`stderr`).

### Function examples

{% list tabs group=programming_language %}

- Node.js {#node}

    **package.json**
    ```json
    {
      "name": "server-app",
      "version": "1.0.0",
      "dependencies": {
        "winston": "^3.8.2"
      }
    }
    ```

    **index.js**
    ```js
    const winston = require('winston');

    const logger = winston.createLogger({
        level: 'debug',
        format: winston.format.json(),
        transports: [new winston.transports.Console()],
    });

    module.exports.handler = async function (event, context) {
        logger.info({"message": "My log message", "my-key": "my-value"})
        return {
            statusCode: 200,
            body: 'Hello World!',
        };
    };
    ```

- Python {#python}

    **requirements.txt**
    ```
    python-json-logger==2.0.4

    ```

    **index.py**
    ```python
    import logging
    from pythonjsonlogger import jsonlogger


    class YcLoggingFormatter(jsonlogger.JsonFormatter):
        def add_fields(self, log_record, record, message_dict):
            super(YcLoggingFormatter, self).add_fields(log_record, record, message_dict)
            log_record['logger'] = record.name
            log_record['level'] = str.replace(str.replace(record.levelname, "WARNING", "WARN"), "CRITICAL", "FATAL")

    logHandler = logging.StreamHandler()
    logHandler.setFormatter(YcLoggingFormatter('%(message)s %(level)s %(logger)s'))

    logger = logging.getLogger('MyLogger')
    logger.propagate = False
    logger.addHandler(logHandler)
    logger.setLevel(logging.DEBUG)

    def handler(event, context):
        logger.info("My log message", extra={"my-key": "my-value"})
        
        return "Hello, world!"
    ```
- Go {#go}

    **index.go**
    ```go
    package main

    import (
        "context"
        "go.uber.org/zap"
    )

    type Response struct {
        StatusCode int         `json:"statusCode"`
        Body       interface{} `json:"body"`
    }

    func Handler(ctx context.Context) (*Response, error) {
        config := zap.NewProductionConfig()
        config.DisableCaller = true
        config.Level.SetLevel(zap.DebugLevel)
        logger, _ := config.Build()
        defer logger.Sync()

        logger.Info(
            "My log message",
            zap.String("my-key", "my-value"),
        )

        return &Response{
            StatusCode: 200,
            Body:       "Hello, world!",
        }, nil
    }
    ```

- Java {#java}

    **pom.xml**
    ```xml
    ...
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
        <version>2.19.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.19.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-layout-template-json</artifactId>
        <version>2.19.0</version>
    </dependency>
    ...
    ```

    **log4j2.xml**
    ```xml
    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration>
        <Appenders>
            <Console name="Console" target="SYSTEM_OUT">
                <JsonTemplateLayout eventTemplateUri="classpath:YcLoggingLayout.json"/>
            </Console>
        </Appenders>
        <Loggers>
            <Root level="TRACE">
                <AppenderRef ref="Console"/>
            </Root>
        </Loggers>
    </Configuration>
    ```

    **YcLoggingLayout.json**
    ```json
    {
      "message": {
        "$resolver": "message",
        "stringified": true
      },
      "level": {
        "$resolver": "level",
        "field": "name"
      },
      "logger": {
        "$resolver": "logger",
        "field": "name"
      },
      "labels": {
        "$resolver": "mdc",
        "flatten": true,
        "stringified": true
      },
      "tags": {
        "$resolver": "ndc"
      }
    }
    ```

    **Handler.java**
    ```java
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    import org.apache.logging.log4j.ThreadContext;

    import java.util.function.Function;

    public class Handler implements Function<String, String> {

        private static final Logger logger = LogManager.getLogger();

        @Override
        public String apply(String s) {
            ThreadContext.put("my-key", "my-value");
            logger.info("My log message");
            ThreadContext.clearAll();
            return "Hello, world!";
        }
    }
    ```

{% endlist %}