Skip to content

Fields

For extra context, every log is accompanied with an optional fields dictionary.

It's encouraged to put changing parts of your log messages inside of fields argument as it makes it simpler to match on such attributes while creating workflows. For example, instead of logging message "Timezone changed from X to Y" log "Timezone changed" message with fields two fields: from and to.

Kotlin
Logger.log(LogLevel.INFO, mapOf("from" to "UTC", "to" to "PST")) { "Timezone changed" }
Java
HashMap fields = new HashMap();
fields.put("from", "UTC");
fields.put("to", "PST");

Logger.log(LogLevel.INFO, fields, () -> "Timezone changed");
Swift
Logger.log(level: .info, "Timezone changed", fields: ["from": "UTC", "to": "PST"])
Objective-C
[CAPLogger logWithLevel:LogLevel.info message:@"Timezone changed" fields: @{@"from": @"UTC", @"to": @"PST"}];
JavaScript
import { info } from '@bitdrift/react-native';

info('Timezone changed', { from: 'UTC', to: "PST" });
JavaScript
import { info } from '@bitdrift/react-native';

info('Timezone changed', { from: 'UTC', to: "PST" });

There are three different ways to attach fields to logs. Below is the list, in order of priority, indicating which methods have precedence over others (fields provided by methods higher in the list override those provided by methods lower in the list):

  • Using the fields argument in a specific log(...) method: This gives the greatest level of control on a per-log basis but has the highest performance impact. Refer to Custom Fields for more details.
  • Using the addField(...) method: This attaches fields to all logs emitted by the SDK after the method call. It is the most performant way to add fields to logs. Refer toField Addition and Removal Methods for more details.
  • Implementing FieldsProvider protocol/interface: This approach is more flexible but less direct. Refer to Field Providers for more details.

Default Fields

Every log emitted by the SDK is tagged with extra out-of-the-box attributes related to the device and app state. Below is the their list:

Field Name Field Key Notes
App Identifier app_id
App Version app_version The value of the versionName attribute of the PackageInfo
App Version Code _app_version_code The value obtained using getLongVersionCode or versionCode accessors.
Carrier carrier
Foreground foreground
Locale _locale The string value of the getLocales method call.
Log Level level
Network Type network_type
Network Quality _network_quality Expose if the app is Offline based on its ability to reach the bitdrift ingest API. The field is not shown if the app is online. Note that any successful network request will identify the app as online
OS os
OS Version os_version The value of RELEASE property.
Radio Type radio_type
Field Name Field Key
App Identifier app_id
App Version app_version The value stored under the CFBundleShortVersionString key of the info dictionary of the main Bundle.
Build Number _build_number The valued stored under the kCFBundleVersionKey key of the infoDictionary property of the main Bundle.
Foreground foreground
Locale _locale The value of the Locale.current.identifier property.
Log Level level
Network Type network_type
Network Quality _network_quality Expose if the app is Offline based on its ability to reach the bitdrift ingest API. The field is not shown if the app is online. Note that any successful network request will identify the app as online
OS os
OS Version os_version The value of systemVersion property.
Radio Type radio_type

React Native supports all the out of the box fields provided by the platform the app is running on, see the iOS and Android sections for more details.

Custom Fields

The SDK allows all but the following field names to be used by the customers of the SDK:

  • Fields with keys that conflict with keys of the default fields emitted by the SDK. This is enforced for all fields and offending fields are dropped (a warning message is printed in the console when that happens).
  • Fields whose names start with "_" (underscore character). This is enforced for global fields and offending fields are dropped (a warning message is printed in the console when that happens). The rule is going to be enforced for fields arguments of custom logs in the future.

Providing Fields with Logging Methods

You can attach arbitrary fields to logs emitted by and with the use of the SDK.

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" }
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");
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"])
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"}];
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' });
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' });

Global Fields

The SDK supports two ways of adding log fields to attach to every emitted log. Each of these methods manages a separate pool of logs, ensuring that the method of managing fields does not impact those managed by the other method.

Note

Global fields are not persisted to disk and thus do not survive process restarts, so it is possible for these values to change within a session depending on your session management strategy.

Field Addition and Removal Methods

The logger exposes addField(...) and removeField(...) methods, which can be used to control the list of global fields added to emitted logs.

Kotlin
Logger.addField("key", "value")
Logger.removeField("key")
Java
Logger.addField("key", "value");
Logger.removeField("key");
Swift
Logger.addField(withKey: "key", value: "value")
Logger.removeField(withKey: "key")
JavaScript
import { addField, removeField } from '@bitdrift/react-native';

addField('key', 'value');
removeField('key');

Field Providers

The start method takes an optional list of fieldProviders where you can implement your own logic for retrieving the data you want to pass.

In cases of key conflicts (where two or more fields share the same key), the earlier a field provider appears in the list of registered providers, the higher priority the field it returns is given.

Kotlin
// Attach your own custom user_id field to each log
class CustomUserIdProvider : FieldProvider {
  override fun invoke(): Fields {
    return mapOf("user_id" to "<your_custom_user_id>")
  }
}

Logger.start(
  // ...
  fieldProviders = listOf(CustomUserIdProvider()),
)
Java
// Attach your own custom user_id field to each log
class CustomUserIdProvider implements FieldProvider {
  @Override
  public Map<String, ? extends String> invoke() {
    return Collections.singletonMap("user_id", "<your_custom_user_id>");
  }
}

Logger.start(
  // ...
  Collections.singletonList(new CustomUserIdProvider()) //fieldProviders
);
Swift
// Attach your own custom user_id field to each log
final class CustomUserIdProvider: FieldProvider {
  func getFields() -> Fields {
    ["user_id": "<your_custom_user_id>"]
  }
}

Logger.start(
  // ...
  fieldProviders: [CustomUserIdProvider()],
)

Tip

user_id is a one-of-a-kind special field. Providing it as shown above will cause it to appear in the Timeline header.