# 3. Self-defined log

TL;DR: straight to the [example ](#id-3.-vd):)))

### 1. Log design <a href="#id-1.-thiet-ke-ban-ghi" id="id-1.-thiet-ke-ban-ghi"></a>

User data records, for ease of processing and statistics, need to be classified based on events, with records of each event having a similar structure to each other. With C#, events are considered equivalent to classes, and the structure is defined by the fields of the class.

To declare an event log, we create a class that inherits from one of two classes:

* PlainFlexLog: The original log class, does not provide any additional information beyond the fields in the class.
* BaseFalconLog: Basic class log, providing basic additional information about the player, including:
  * abTesting: Get it automatically according to FalconAbTesting, see more [here](https://falcon-game-studio.gitbook.io/falcon-sdk/falcon-core/2.-remoteconfig-and-ab-testing)
  * accountId: If there is nothing special, the default is deviceId
  * appVersion
  * deviceId
  * gameId, gameName
  * installDay:saved locally, will be reset if deleted and reinstalled
  * level: updated according to levelLog, or the user sets the value himself
  * platform
  * retentionDay: calculated by installDay
  * clientCreateDate: log object creation time, in epoch time millis
  * apiId: Total number of Objects of the current event ever created (including current log)
  * country : Automatically collect the sender's IP and country on the server side

In the newly created class, we declare the necessary additional information fields. Then, when we need to log, we create a new Object of that event and then call the send() function.

Note:

* class should have attribute \[Serializable] and fields should be public
* Event generation is not performed with the \[RuntimeInitializeOnLoadMethod] attribute functions because the SDK background values are initialized here.
* The abTesting value will be taken at the time of creating the log object (which may not be the latest value from the server), so if the abTesting value is important for statistics, check FalconConfig.UpdateFromNet before creating the log. Refer to abTesting [here](https://falcon-game-studio.gitbook.io/falcon-sdk/falcon-core/2.-remoteconfig-and-ab-testing).
* <mark style="color:orange;">Do not create a field named createDate or names similar to this phrase</mark> because the server inserts a field with such a name to manage records. If you want to assign a timestamp to the record, please fill in the clientCreateDate field already available in the log. If a record violates this, it will be automatically removed.

### 2. Type of data field <a href="#id-2.-ve-kieu-cua-truong-du-lieu" id="id-2.-ve-kieu-cua-truong-du-lieu"></a>

A log field can have the following data types:

* Number
  * Whole number: byte, short, int, long, bigInt
  * Real number: float, double, bigDecimal
* DateTime
* Boolean
* String
* Object (should be Serializable): Objects when uploaded to the database will be flattened, separated by the character '$'

When a record is sent to the server, the entire metadata about the types of data fields in the record will be saved to process future records, so later records with data types that differ from the previous record will be saved. considered invalid and removed (Number data types will be automatically converted to each other, so you don't have to worry about cases like int not accepting floats :v). Therefore, if you want to change the data type of the field (due to testing, actual requirements, etc.), we cannot support this part and it is best for you to rename the field and treat it as a new field. \_.|||||

### 3. Example <a href="#id-3.-vd" id="id-3.-vd"></a>

Requirement: For each player when installing the game, we need to log the event to see if the installed player is organic or inorganic. This event log needs to provide information including player type (organic/inorganic), deviceId, playerMediationId, installation time, appVersion,... This information can be provided from the Unity system, along with the mediation that the game is using, and mediation needs to be initialized before calling the function.

#### 3.1. Class declare <a href="#id-3.1.-khai-bao-class" id="id-3.1.-khai-bao-class"></a>

```
[Serializable]
public class PlayerTypeLog : BaseFalconLog
{
    public String playerMediationId;
    public String playerType;
    
    public PlayerTypeLog(string playerMediationId, string playerType)
    {
        this.playerMediationId = playerMediationId;
        this.playerType = playerType;
    }
}
```

The class that inherits BaseFalconLog will provide a lot of automatic information such as deviceId, installation time, etc. There are only 2 pieces of information: playerMediationId and playerType that need to be actively collected from the Mediation side.

#### 3.2. Using <a href="#id-3.2.-su-dung-trong-code" id="id-3.2.-su-dung-trong-code"></a>

```
public class PlayerDataCollectObject : MonoBehaviour
{
    private IEnumerator Start()
    {
        //Check to see if this player has ever posted a log
        //Because each player only needs to log in once, that's enough
        if (PlayerPrefs.HasKey("Player Type Detect"))
        {
            yield break;
        }
        
        //Init the Mediation
        MediationService.Init();

        //Wait for Mediation to complete initialization
        while (!MediationService.InitComplete) yield return null;

        //Get value from Mediation
        string playerMediationId = MediationService.PlayerMediationId;
        var playerMediationType = MediationService.IsPlayerOrganic ? "Organic" : "InOrganic";

        //Create log and send
        new PlayerTypeLog(playerMediationId, playerMediationType).Send();
        
        //Save the key to PlayerPrefs to avoid duplicate logs in the future
        PlayerPrefs.SetString("Player Type Detect", "Detected");
    }
}
```
