# 1. Basic logs

<mark style="color:red;">Please remember to add your game to data4game. Lacking to do so will prevent data from being recorded, making charts unavailable. Further instructions can be found</mark> [here](https://falcon-game-studio.gitbook.io/falcon-sdk/general-guide/how-to-add-game).

The user needs to call functions at certain points in the game to send information to the data analysis server, which can then view the analyzed data at [data4game.com](http://data4game.com/).&#x20;

Instructions for viewing detailed figures are available at <https://falcon-game-studio.gitbook.io/falcon-bigdata/giai-thich-bieu-do/tong-quan>

Currently, API is providing 8 functions that call statistics:

* [`LevelLog`](#id-1.1.-level-log): game level statistic.
* [`​InAppLog`](#id-1.2.-inapp-log): player in-app statistic.
* [`AdsLog`](#id-1.3.-a-dvertisement-log): player ad watch statistic.
* [`ResourceLog`](#id-1.4.-resource-log): player resource get/use statistic.
* [`SessionLog`](#id-1.5.-session-log): player playtime statistic.
* [`FunnelLog`](#id-1.6.-funnellog): designed to hierarchize players according to any property.
* [`PropretyLog`](#id-1.7.-property-log) : Dynamic statistics of events/features/items players have unlocked, designed for players to define a dynamic graph themselves.
* [`ActionLog`](#id-1.8.-action-log): Statistics the movement between 2 player behaviors, designed to track the order and analyze user behavior.

At the same time, there are 2 logs that are performed automatically:

* RetentionLog: player retention statistic.
* UserTotalTimeLog: statistics of the player's total gaming time per play.

Details of the above automatic log functions can be viewed at [Automatic Logs](https://falcon-game-studio.gitbook.io/falcon-sdk/falcon-analytics/2.-automatic-logs).

<mark style="color:orange;">Note:</mark>

* The parameters in the functions, if there is no separate note, then all need to enter a value other than null.
* The strlen in the note (if any) is the length of the input string value.
* If the player does not have internet access when playing the game, i.e. cannot send logs to the server, the SDK will automatically save and send when the network is restored, but this saving only contains a maximum of 100 messages.
* If you need to change a specific statistical data field, you can refer [PlayerParams](https://falcon-game-studio.gitbook.io/falcon-sdk/falcon-analytics/4.-fplayerinforepo/version-2.x.x).
* As of version 2.2.2, we no longer remove error logs but will try to return the value to the correct format. This is done in order not to ignore any event of the player, helping to ensure the full push of the data. <mark style="color:red;">However, when using these versions, users must ensure the logic of LevelLog and FunnelLog themselves.</mark>

## 1.1. Level log

Call every time the player finishes a new level in the game. After creating the record, you need to call the Send() function to send the message ( `new FLevelLog(...).Send();`)

The constructor is:

```
public FLevelLog(int currentLevel, string difficulty, LevelStatus status, TimeSpan duration, int wave = 0)
```

* `int currentLevel`: the playing level
* `string difficulty`: the difficulty of the playing level(*`"easy"`, `"hard"`, `"no hope"`*, ...)
* `LevelStatus status`: the playing status when the level end
  * *`LevelStatus.Pass` -* first pass
  * *`LevelStatus.Fail` -* Play the level failed without ever passing the level
  * *`LevelStatus.ReplayPass`* - passed the level before, now play again and still pass the level
  * *`LevelStatus.ReplayFail`* - passed the level before, now play again and fail the level
* `TimeSpan duration`: the time the player spends on the level.
* `int wave`: the number of waves the player passes before failing, or the total number of waves in the level, if the player passes (default is 0, meaning no need to pass if not interested, or the game level has no waves)

VD:

* *`new FLevelLog(2, "normal", LevelStatus.pass, TimeSpan.FromSeconds(69), 5).Send()`*
* *`new FLevelLog(50, "hard", LevelStatus.fail, TimeSpan.FromMinutes(6), 9).Send()`*

<mark style="color:red;">**Basic logic:**</mark>

* If the player have never surpassed the level playing, if:
  * LevelStatus == Pass : Send log to the server and save the level value for future processing.
  * LevelStatus == Fail : Send log to server.
  * LevelStatus == ReplayPass : Illogical logic, developers need to make sure this does not happen.
  * LevelStatus == ReplayFail : Illogical logic, developers need to ensure this does not happen.
* If the player have ever exceeded the level you are playing, if:
  * LevelStatus == Pass : Change to LevelStatus.ReplayPass.
  * LevelStatus == Fail : Set to LevelStatus.Replay Fail.
  * LevelStatus == ReplayPass : Reasonable logic, no adjustments needed.
  * LevelStatus == ReplayFail : Reasonable logic, no adjustments needed.

<mark style="color:red;">**Note**</mark>**:**&#x20;

* Level log with Status = LevelStatus.Pass shouldonly created once
* A Level will be evaluated for uniqueness by the LevelId value, default is currentLevel + "Difficulty" + difficulty. If your game evaluates levels differently, please override this function.
* The level value from the level log,  if status=Pass, will be saved and used to update the maxPassedLevel value.

## 1.2. InApp log

Call after the player makes an InApp purchase in the game. After creating the record, you need to call the Send() function to send the message ( `new FInAppLog(...).Send();`)

The constructor is:

```
public FInAppLog(string productId, decimal localizedPrice, string isoCurrencyCode, string where, string transactionId, string purchaseToken = null, int currentLevel = 0)
```

* `string productId`: ID of the in-app package
* `decimal localizedPrice`: the product price, following local currency  (hasn't been multiplied with 0.7, the server will do this automatically)
* `string isoCurrencyCode`: Which currency do players deposit through <mark style="color:red;">(Following</mark> [<mark style="color:blue;">ISO 4217 currency codes</mark>](https://www.iban.com/currency-codes)<mark style="color:red;">, means "USD" instead of "$", "dollar", "usDollar", ...)</mark>
* `string where`: Deposit location (Recharge in the shop / in the pop-up package at the end of the level,...)
* `string transactionId`:&#x20;
* `string purchaseToken`: The token that Google Play returns after the transaction(default is null for IOS)
* `int currentLevel`: the level playing when the purchase event occurs (default is 0 if not caring)

VD:

* *`new FInappLog("pack_gem_20usd", 30.99m, "USD", "Level_45", "GPA.330...528").Send()`*
* *`new FInappLog("daily_pack_1", 8400m, "KRW", "shop", "GPA.335...779", "fcdfhoogaefl ... HMtQQ", 8).Send()`*
* Example for Inapp log for UnityEngine.Purchasing.Product - Unity default purchase info:

<figure><img src="https://1219926239-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FjXDVMhWRiIUCExjfRIcD%2Fuploads%2FQoc49qf61rBTKRDkatyM%2Fimage.png?alt=media&#x26;token=f52a52a0-483f-4100-820c-a72e49d670f2" alt=""><figcaption></figcaption></figure>

## 1.3. Advertisement log

Call after the player finishes watching ads in the game. After creating the record, you need to call the Send() function to send the message ( `new FAdLog(...).Send();`)

The constructor is:

```
public FAdLog(AdType type, string adWhere, int currentLevel = 0, double? adLtv = null)
```

<mark style="color:red;">`This constructor is deprecated for only provide information about ad impression, lacking ad revenue data. If using this constructor, you won't be able to see the ad revenue.`</mark>

* `AdType type`: Type of ad watched (*`AdType.interstitial`, `AdType.reward`)*
* `string adWhere`: Where to view AD (View in the shop, view in the level to revive/get rewards,...)
* `int currentLevel`: the level playing when the ad watch event occurs (default is 0 if not caring)
* `double? adLtv:` lifetime value- total ad revenue of the player (default is null if not caring)

or:

```
public FAdLog(AdType type, string adWhere, string adPrecision, string adCountry, double adRev, string adNetwork, string adMediation, int currentLevel = 0, double? adLtv = null)
```

* `AdType type`: Type of ad watched (*`AdType.interstitial`, `AdType.reward`)*
* `string adWhere`: Where to view AD (View in the shop, view in the level to revive/get rewards,...)
* `string adPrecision`: source revenue of the ad&#x20;
  * **BID** – The revenue provided as part of the real-time auction (Not supported by FAN bidders)
  * **RATE** – The manual price the publisher assigned to an instance in the ironSource platform
  * **CPM** – Calculation based on historical instance performance, performed by ironSource platform
* `string adCountry`: country code ISO of the player, following the mediation
* `double adRev`: revenue of the ad
* `string adNetwork`: network providing the ad
* `string adMediation`: mediation used
* `int currentLevel:` the level playing when the ad watch event occurs (default is 0 if not caring)
* `double? adLtv:` lifetime value- total ad revenue of the player (default is null if not caring)

EXP:&#x20;

* *`new FAdLog(AdType.interstitial, "Video_RandomEndgame", 15).Send()`*
* *`new FAdLog(AdType.reward, "Video_Revival", "BID", "PH", 0.001876, "admob", "IronSrc", 15, 16.57).Send()`*
* Example <mark style="color:orange;">logging ad through IronSource impression event:</mark>

```
/*
* IronSourceEvents.onImpressionDataReadyEvent += ImpressionDataReadyEvent
*/

public static void ImpressionDataReadyEvent(IronSourceImpressionData impressionData)
 {
     if (impressionData.revenue == null) return;
     AdType type;
     switch (impressionData.adUnit)
     {
         //private const string InterstitialIrs = "interstitial"
         case InterstitialIrs:
             type = AdType.Interstitial;
             break;
         //private const string RewardedVideoIrs = "rewarded_video"
         case RewardedVideoIrs:
             type = AdType.Reward;
             break;
         //private const string BannerIrs = "banner";
         case BannerIrs:
             type = AdType.Banner;
             break;
         default:
             return;
     }
    //In the game, you need to save the current ad call location,
    //Let's assume GameManager will save that location
     string adWhere = GameManager.AdWhere;

    if (type == AdType.Banner)
     {
         BannerLogStatistic.Instance.Log(adWhere, impressionData.precision, impressionData.country,
             impressionData.revenue.Value, impressionData.adNetwork, "IronSrc", impressionData.lifetimeRevenue);
         return;
     }
     
     new FAdLog(type, adWhere , impressionData.precision,
         impressionData.country, impressionData.revenue.Value, impressionData.adNetwork, "IronSrc",
         impressionData.lifetimeRevenue).Send();
 }
```

* Example l<mark style="color:orange;">ogging ad through Max impression event:</mark>

Copy

```
/*
* MaxSdkCallbacks.Interstitial.OnAdRevenuePaidEvent += OnAdRevenuePaidEvent;
*/

public static void OnAdRevenuePaidEvent(MaxSdkBase.AdInfo impressionData)
{
    AdType type;
    witch (impressionData.AdFormat)
    {
    // private const string InterstitialMax = "INTER";
        case InterstitialMax:
            type = AdType.Interstitial;
            break;
    //private const string RewardedVideoMax = "REWARDED";
        case RewardedVideoMax:
            type = AdType.Reward;
            break;
    //private const string BannerMax = "BANNER";
        case BannerMax:
            type = AdType.Banner;
            break;
        default:
            return
    }

    //In the game, you need to save the current ad call location,
    //Let's assume GameManager will save that location
     string adWhere = GameManager.AdWhere;
     
    if (type == AdType.Banner)
    {
        BannerLogStatistic.Instance.Log(adWhere, impressionData.RevenuePrecision, "",
            impressionData.Revenue, impressionData.NetworkName, "Max");
        return;
    }

    new FAdLog(type.Value, adWhere, impressionData.RevenuePrecision,
        "", impressionData.Revenue, impressionData.NetworkName, "Max").Send();
}
```

<mark style="color:orange;">Note</mark>: logging banner events should use BannerLogStatistic to optimize

## 1.4. Resource log

Call every time the player receives/uses resources in the game. After creating the record, you need to call the Send() function to send the message ( `new FResourceLog(...).Send();`)

The constructor is:

```
public FResourceLog(FlowType flowType, string itemType, string currency, string itemId, long amount, int currentLevel = 0)
```

* `FlowType flowType`: Player gets/uses resources
  * *FlowType.source* - gets
  * *FlowType.sink* - uses&#x20;
* `string itemType`: The resource supplement/consumption (in general)
* `string itemId`: The resource supplement/consumption (in specific)&#x20;
* `string currency`: Type of resources players get (gem, gold, crystal, ...)&#x20;
* `long amount`: The amount of resources the player receives
* `int currentLevel`: the level playing when the resource event occurs (default is 0 if not caring)

EXP:&#x20;

* *`new FResourceLog(13, FlowType.source, "login", "login","gem", 2).Send()`*
* *`new FResourceLog(5, FlowType.sink, "upgrade", "upgrade_weapon","gold", 4185).Send()`*

Note: itemId can be thought of as subType for itemType, a more detailed annotation for the resource (for example, for the same upgrade in itemType, but itemId will be more detailed such as upgradeWeapon / upgradeCharacter / ...), for convenient analysis charting.

## 1.5. Session log

Calling each end measures the amount of time the player spends on a feature (event, ...). After creating the record, you need to call the Send() function to send the message ( `new FSessionLog(...).Send();`)

The constructor is:

```
public FSessionLog(string gameMode, TimeSpan sessionTime, int currentLevel = 0)
```

* `string gameMode`: Feature name.
* `TimeSpan sessionTime`: Total time the player uses for the feature.
* `int currentLevel`: the level playing when the session event occurs (default is 0 if not caring)

EXP:&#x20;

* *`new FSessionLog("event_christmas_day1_2021", TimeSpan.FromSeconds(153)).Send()`*
* *`new FSessionLog("daily_gameplay", TimeSpan.FromMinutes(15)).Send()`*

## 1.6. FunnelLog

It is a log used to draw a funnel chart, which creates blocking levels and filters players showing the percentage of players who passed each level. After creating the record, you need to call the Send() function to send the message ( `new FFunnelLog(...).Send();` )

The constructor is:

```
public FFunnelLog(string funnelName, string action, int priority, int currentLevel = 0)
```

* `string funnelName`: The name of the funnel chart
* `string action`: Name of the level in the funnel
* `int priority`: The order of the level in the funnel&#x20;
* `int currentLevel`: the level playing when the funnel event occurs (default is 0 if not caring)

EXP:&#x20;

* *`new FFunnelLog("inapp_purchase", "whale", 17, 20).Send()`*
* *`new FFunnelLog("story_line", "chapter_7", 53).Send()`*

<mark style="color:red;">**Basic logic:**</mark>

* If the priority is greater than 0, and there has never been a log of the priority before with the same funnelName (like the priority is logged as 5 with funnelName as "level" but the system recognizes that it has never logged with priority 4 with funnelName as "level") -> log in the wrong order -> the log sending needs to be done in the correct order of priority 0 -> 1 -> 2 -> 3 -> ...
* If the system recognizes the previous current log priority with the same funnelName (currently log funnelName is "level", priority is 4 but in the past also logged funnelName is "level", priority is 4), then the current log will be evaluated as duplicate.

<mark style="color:red;">Note:</mark> According to the nature of the funnel chart, players must pass each block level in ascending order, so the player's log must also be done in turn, for example, if the player has logged the funnel "play\_date" with priorities 0 -> 1 -> 2 -> 3 (ie currently at level 3), then the player will have to log the funnel "play\_date" at priority 4, not at other levels such as 5, 9, 2, ...

## 1.7. Property log

Call every time the player unlocks 1 feature, item, event,... This is a log designed to help users dynamically define the value they want to synthesize. After creating the record, you need to call the Send() function to send the message ( `new FPropertyLog(...).Send();`)

The constructor is:

```
public FPropertyLog(string pName, string pValue, int priority, int currentLevel = 0)
```

* `string pName`: Name of the type of feature/item unlocked
* `string pValue`: Type of Feature/Item unlocked&#x20;
* `int priority`: Hierarchy, the order of the property, is used by the server to sort and chart
* `int currentLevel`: the level playing when the property event occurs (default is 0 if not caring)

EXP:&#x20;

* *`new FPropertyLog("weapon", "holy_moly_whack_a_moley_legendary_cannon", 99999).Send()`*
* *`new FPropertyLog("level", "super_secret_hidden_level",5,69).Send()`*
* *`new FPropertyLog("mission", "extremly_difficult_mission_that_nobody_can_complete",18).Send()`*

## 1.8. Action log

<mark style="color:red;">We are testing this log more closely, so in the process, the call of this log may be different on the doc.</mark>

Call when the player has just switched between two actions/events/scene/panel/etc. This is a type of log designed to track the order and analyze user behavior. After creating the record, you need to call the Send() function to send the message ( `new FActionLog(...).Send();`)

The constructor is:

```
FActionLog(string actionName)
```

* `string actionName`: The player's current actions.

EXP:&#x20;

* *`new FActionLog("shop").Send()`*
* *`new FActionLog("weapon_upgrade_scene").Send()`*
* *`new FActionLog("đạp_zịt_game").Send()`*
