# Report Logs

{% hint style="warning" %}
**Privacy Policy**

It is highly recommended to mention in your privacy policy that you may be collecting logging data in order to assist troubleshooting bugs.
{% endhint %}

A variety of logs types are sent with each crash or bug report. They appear within each report in your Luciq dashboard, as shown below. Log collection stops when Luciq is shown.

We support the following types of logs:

* [User Steps](#user-steps)
* [Network Logs](#network-logs)
* [Luciq Logs](#luciq-logs)
* [Console Logs](#console-logs)
* [User Events](#user-events)

![](/files/929c2dffc173a10c5e91930a7c67f249a449b57a)

### User Steps

Luciq can help you reproduce issues by tracking each step a user has taken until a report is sent. Note that the maximum number of user steps sent with each report is 100.

{% code title="JavaScript" %}

```
Luciq.setTrackUserSteps(true);
```

{% endcode %}

User steps will be formatted differently depending on the OS of your app. More details regarding the formatting can be found [here for iOS](/ios/setup-luciq-for-ios/custom-settings/logs-and-profiling/report-logs.md#user-steps) and [here for Android](/android/set-up-luciq-for-android/logs-and-profiling/report-logs-for-android.md#user-steps).

### Network Logs

Luciq automatically logs all network requests performed by your app from the start of the session. Requests details, along with their responses, are sent with each report. Luciq will also show you an alert at the top of the bug report in your dashboard when network requests have timed-out or taken too long to complete. Note that the maximum number of network logs sent with each report is 1,000.

![2040](/files/d1afab24ea083e99c2830f96770910f33920010d)

An example of network request logs in the Luciq dashboard.

#### Importing Network Logger

First, you'll need to import the Network Logger in order to be able start logging network requests. You can use the below statement to import it.

{% code title="JavaScript" %}

```javascript
import { NetworkLogger } from '@luciq/react-native';

Luciq.init({
  token: 'APP_TOKEN',
  invocationEvents: [InvocationEvent.shake],
  networkInterceptionMode: NetworkInterceptionMode.javascript, // default
});
```

{% endcode %}

#### Native network requests capturing

You can apply this using `NetworkInterceptionMode.native`

Network traffic is captured at the native SDK layer (URLSession on iOS, OkHttp on Android).\
It captures traffic that may not go through XMLHttpRequest (e.g., requests made directly from native modules).

When using native mode, filtering and obfuscation should be configured through native code\
rather than JavaScript. However, if JS-side filtering or obfuscation handlers are set, the\
SDK bridges native network snapshots to JavaScript for processing before they are stored.

#### Apollo Client GraphQL Operations Integration

The SDK provides built-in support for Apollo Client GraphQL operations through the `apolloLinkRequestHandler` Apollo Link. This link automatically captures GraphQL operation names so they appear alongside network logs rather than showing only the generic GraphQL endpoint URL.

**Setup**

Add `NetworkLogger.apolloLinkRequestHandler` to your Apollo Client link chain:

```javascript
import { ApolloClient, InMemoryCache, HttpLink, from } from '@apollo/client';
import { NetworkLogger } from 'luciq-reactnative';

const httpLink = new HttpLink({ uri: 'https://api.example.com/graphql' });

const client = new ApolloClient({
  link: from([NetworkLogger.apolloLinkRequestHandler, httpLink]),
  cache: new InMemoryCache(),
});
```

**How It Works**

1. The Apollo Link intercepts each outgoing operation and injects a custom header (`lcq-graphql-header`) with the value of `operation.operationName`.
2. When the XHR interceptor processes the completed request, it reads this header, assigns the value to the `gqlQueryName` field on the network log, and removes the header from the logged request headers so it does not appear in the captured output.
3. If the response body contains an `errors` field (standard GraphQL error format), the SDK automatically sets `serverErrorMessage` to "GraphQLError".

**Disclaimer**

* On iOS, `gqlQueryName` is sent to bugs, crashes and APM traces.
* On Android, `gqlQueryName` is sent to APM only.

#### Omitting Requests

You can omit requests from being logged based on either their request or response details.

`setRequestFilterExpression` allows you to specify an expression to be evaluated against every request and response to determine if the request should be included in logs or not.

{% code title="JavaScript" overflow="wrap" %}

```javascript
NetworkLogger.setRequestFilterExpression('network.requestHeaders[\'accept\'] === \'application/json\'')
```

{% endcode %}

The code above excludes all requests made to URLs that have request header `accept` set to `application/json`.

The network parameter is the full `NetworkData` object, so the expression has access to:

| Field                        | Type                     | Description                      |
| ---------------------------- | ------------------------ | -------------------------------- |
| `network.id`                 | `string` (readonly)      | Network object's id              |
| `network.url`                | `string`                 | Request URL                      |
| `network.method`             | `string`                 | HTTP method                      |
| `network.requestBody`        | `string`                 | Request body                     |
| `network.requestBodySize`    | `number`                 | Body size in bytes               |
| `network.responseBody`       | `string \| null`         | Response body                    |
| `network.responseBodySize`   | `number`                 | Response body size               |
| `network.responseCode`       | `number`                 | HTTP status code                 |
| `network.requestHeaders`     | `Record<string, string>` | Request headers (lowercase keys) |
| `network.responseHeaders`    | `Record<string, string>` | Response headers                 |
| `network.contentType`        | `string`                 | Response content-type            |
| `network.errorDomain`        | `string`                 | Error domain if failed           |
| `network.errorCode`          | `number`                 | Error code if failed             |
| `network.startTime`          | `number`                 | Request start timestamp          |
| `network.duration`           | `number`                 | Duration in ms                   |
| `network.gqlQueryName`       | `string \| undefined`    | GraphQL operation name           |
| `network.serverErrorMessage` | `string`                 | Server error message             |
| `network.requestContentType` | `string`                 | Request content-type             |

#### Obfuscating Data

**Requests**

You can obfuscate sensitive user data in requests, like authentication tokens for example, without filtering out the whole request.

{% code title="JavaScript" overflow="wrap" %}

```javascript
NetworkLogger.setNetworkDataObfuscationHandler(async (networkData) => {
      networkData.requestHeaders = {'Content-Type': 'application/json', 'TestHeader': 'some-header2'}
      return networkData;
    });
```

{% endcode %}

To clarify, the `networkData` object utilized within the obfuscation handlers encapsulates the same data fields mentioned previously, such as request details, response contents, and relevant metadata. This includes fields like `network.method`, `network.responseBody`, `network.responseHeaders`, and more, allowing for detailed manipulation and obfuscation of network transaction data.

**Responses**

As with requests, the `responseBody` and `responseHeader`, can be modified for obfuscation purposes before they are logged.

{% code title="JavaScript" overflow="wrap" %}

```javascript
NetworkLogger.setNetworkDataObfuscationHandler(async (networkData) => {
      networkData.responseBody = {};
      networkData.responseHeaders = {};
      return networkData;
    });
```

{% endcode %}

**Disclaimer**

Please note that when using native interception mode on iOS, the obfuscation handler receives a reduced set of `NetworkData`. Fields such as `method`, `contentType`, `duration`, and `startTime` may be zeroed out or empty, as they are internally managed by the native SDK. This could limit the information available for obfuscation or logging purposes.

#### Progress Event Handler

If you'd like to execute a block of code whenever a network request is made, you can use the event handler found below. This handler returns the total bytes, as well as, the loaded bytes so far.

{% code title="JavaScript" overflow="wrap" %}

```javascript
NetworkLogger.setProgressHandlerForRequest((total, loaded) => {
});
```

{% endcode %}

#### Disabling Requests

Network request logging is enabled by default if it's included in your [plan](https://www.luciq.ai/pricing). To disable it, use the following method.

{% code title="JavaScript" %}

```javascript
NetworkLogger.setEnabled(false);
```

{% endcode %}

### Luciq Logs

You can log messages throughout your application's lifecycle to be sent with each report. Note that the maximum number of Luciq logs sent with each report is 1,000.

{% code title="JavaScript" %}

```javascript
Luciq.logVerbose("Message to log")
Luciq.logInfo("Message to log")
Luciq.logDebug("Message to log")
Luciq.logError("Message to log")
Luciq.logWarn("Message to log")
```

{% endcode %}

### Console Logs

Luciq captures all console logs and displays them on your dashboard with each report. Note that the maximum number of console logs sent with each report is 1,000 statements with a limit of 5,000 characters for each statement.

#### Native iOS Console Logs

Due to the changes that Apple has made to how logging works, we can only capture native console logs on iOS versions prior to 10.

To work around this issue, you can add the following snippet to your AppDelegate file but outside the scope of the class to allow Luciq to capture native logs automatically on iOS 10 and above.

{% code title="Objective-C" overflow="wrap" %}

```objective-c
#import <Luciq/Luciq.h>

inline void NSLog(NSString *format, ...) {
    va_list arg_list;
    va_start(arg_list, format);
    LCQNSLogWithLevel(format, arg_list, LCQLogLevelNone);
    va_end(arg_list);
}
```

{% endcode %}

### User Events

{% hint style="success" %}
**Best Practices**

Currently the limit of the number of user events sent with each report is 1,000. If you're planning on logging a large amount of unique data, the best practice here would be to use [Luciq Logging](broken://pages/49a66cb40baaec120726c110146c982550761343#section-luciq-logs) instead. The reason for this is that having a very large amount of user events will negatively impact the performance of the dashboard.

Having a large amount of user events will not affect dashboard performance if the user events are not unique.
{% endhint %}

You can log custom user events throughout your application and they will automatically be included with each report. Note that the maximum number of user events sent with each report is 1,000.

{% code title="JavaScript" %}

```javascript
Luciq.logUserEvent("OnFeedbackButtonClicked");
```

{% endcode %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.luciq.ai/react-native/setup-luciq-for-react-native/logs-and-profiling/report-logs.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
