# Custom Logs

You can add custom logs using the log message API. The SDK provides methods for the five supported severity levels: `trace`, `debug`, `info`, `warning`, and `error`.

The logging methods:

* Take an optional `fields` dictionary where you can pass arbitrary attributes using key-value pairs. The `fields` parameter is of type `Map<String, String>` on Android, `[String: Encodable & Sendable]` in Swift (iOS), and `[String: String]` in Objective-C (iOS). Refer to [Fields](fields.md) for more details.
* Take an optional instance of `Throwable` (Kotlin/Java) or `Error` (Swift) as one of its arguments. See [Capturing Errors and Exceptions](#capturing-errors-and-exceptions) for more details.
* On iOS, capture `file`, `line`, and `function` arguments for the location in code where they appear. See [Capturing File, Line and Function](#capturing-file-line-and-function) for more details.

=== "Android (Kotlin)"

    ```kotlin
    Logger.log(LogLevel.INFO, mapOf("key" to "value")) { "Info log" }

    Logger.logTrace(mapOf("key" to "value")) { "Trace log" }
    Logger.logDebug(mapOf("key" to "value")) { "Debug log" }
    Logger.logInfo(mapOf("key" to "value")) { "Info log" }
    Logger.logWarning(mapOf("key" to "value")) { "Warning log" }
    Logger.logError(mapOf("key" to "value")) { "Error log" }
    ```

=== "Android (Java)"

    ```java
    Logger.log(LogLevel.INFO, Collections.singletonMap("key", "value"), () -> "Info log");

    Logger.logTrace(Collections.singletonMap("key", "value"), () -> "Trace log");
    Logger.logDebug(Collections.singletonMap("key", "value"), () -> "Debug log");
    Logger.logInfo(Collections.singletonMap("key", "value"), () -> "Info log");
    Logger.logWarning(Collections.singletonMap("key", "value"), () -> "Warning log");
    Logger.logError(Collections.singletonMap("key", "value"), () -> "Error log");
    ```

=== "iOS (Swift)"

    ```swift
    Logger.log(level: .info, "Info log", fields: ["key": "value"])

    Logger.logTrace("Trace log", fields: ["key": "value"])
    Logger.logDebug("Debug log", fields: ["key": "value"])
    Logger.logInfo("Info log", fields: ["key": "value"])
    Logger.logWarning("Warning log", fields: ["key": "value"])
    Logger.logError("Error log", fields: ["key": "value"])
    ```

=== "iOS (Objective-C)"

    ```objective-c
    [CAPLogger logWithLevel:LogLevel.info message:@"Info log" fields: @{@"key": @"value"}];

    [CAPLogger logTrace:@"Trace log" fields:@{@"key": @"value"}];
    [CAPLogger logDebug:@"Debug log" fields:@{@"key": @"value"}];
    [CAPLogger logInfo:@"Info log" fields:@{@"key": @"value"}];
    [CAPLogger logWarning:@"Warning log" fields:@{@"key": @"value"}];
    [CAPLogger logError:@"Error log" fields:@{@"key": @"value"}];
    ```

=== "React Native"

    ```javascript
    import { info } from '@bitdrift/react-native';

    // Log line with LogLevel of Trace
    trace('Hello world!', { key: 'value' });

    debug('Hello world!', { key: 'value' });
    info('Hello world!', { key: 'value' });
    warning('Hello world!', { key: 'value' });
    error('Hello world!', { key: 'value' });
    ```

## Capturing Errors and Exceptions

All logging methods support passing of an optional `Throwable` (Kotlin/Java) / `Error` (Swift) argument to help with the reporting of errors and exceptions.

=== "Android (Kotlin)"

    For each logged `Throwable` instance, the SDK adds following fields to emitted log:

      * `_error` field with the Java exception name (`it.javaClass.name`) as its value, e.g., `java.io.IOException`.
      * `_error_details` field with exception message (`it.message`) as its value.

    ```kotlin
    Logger.log(LogLevel.INFO, throwable) { "Info log" }

    Logger.logTrace(throwable) { "Trace log" }
    Logger.logDebug(throwable) { "Debug log" }
    Logger.logInfo(throwable) { "Info log" }
    Logger.logWarning(throwable) { "Warning log" }
    Logger.logError(throwable) { "Error log" }
    ```

=== "Android (Java)"

    For each logged `Throwable` instance, the SDK adds following fields to emitted log:

      * `_error` field with the Java exception name (`it.javaClass.name`) as its value, e.g., `java.io.IOException`.
      * `_error_details` field with exception message (`it.message`) as its value.

    ```java
    Logger.log(LogLevel.INFO, throwable, () -> "Info log");

    Logger.logTrace(throwable, () -> "Trace log");
    Logger.logDebug(throwable, () -> "Debug log");
    Logger.logInfo(throwable, () -> "Info log");
    Logger.logWarning(throwable, () -> "Warning log");
    Logger.logError(throwable, () -> "Error log");
    ```

=== "iOS (Swift)"

    For each logged `Error` instance, the SDK adds following fields to emitted log:

      * `_error` field with a localized description of the error (`error.localizedDescription`).
      * `_error_details` field with the description of the error (`String(describing: error)`).
      * `_error_info_X` field(s) for each key-value pair present in [`userInfo`](https://developer.apple.com/documentation/foundation/nserror/1411580-userinfo) dictionary found after casting the passed `Error` instance to `NSError`. For example, for a `userInfo` dictionary like ["foo": "bar"],
      the SDK emits a field with `_error_info_foo` key and `bar` value.

    ```swift

    Logger.log(level: .info, "Info log", error: error)

    Logger.logTrace("Trace log", error: error)
    Logger.logDebug("Debug log", error: error)
    Logger.logInfo("Info log", error: error)
    Logger.logWarning("Warning log", error: error)
    Logger.logError("Error log", error: error)
    ```


## Capturing File, Line, and Function

Logs captured using Swift interfaces on iOS include line file (`#fileID`), line number (`#line`), and function (`#function`) information as their captured fields. That can be disabled by passing the `nil`  values for `file`, `line` and `function` arguments explicitly.

=== "iOS (Swift)"

    ```swift
    // Logs emitted with file, line, and function information attached as fields.
    Logger.log(level: .info, "Info log", fields: ["key": "value"])
    Logger.logInfo(mapOf("key" to "value")) { "Info log" }

    // Disable capturing of file, line, and function information.
    Logger.log(level: .info, "Info log", file: nil, line: nil, function: nil fields: ["key": "value"])
    Logger.logInfo(mapOf("key" to "value"), file: nil, line: nil, function: nil) { "Info log" }
    ```
