Zscaler ZPA logs#

When Zscaler Secure Private Access (ZPA) log ingestion is configured and enabled, Scrutinizer can run both preconfigured and custom reports against the collected ZPA log data.

Follow the steps below to set up a ZPA log stream and configure Scrutinizer to ingest the log flows.

Adding Scrutinizer as a log receiver on ZPA#

Before configuring Scrutinizer to ingest ZPA logs, add it as a log receiver via Configuration & Control > Private Infrastructure > Log Streaming Service > Log Receivers in the ZPA admin interface.

Note

ZPA log ingestion relies on the Log Streaming Service (LSS), which uses an App Connector to forward the logs to a specified Scrutinizer downloader server/collector. If a new App Connector needs to be deployed for this purpose, follow these guides for creating/configuring App Connectors in the ZPA Admin Portal and deploying App Connectors.

After clicking Add, configure the following in the next window:

Log Receiver tab#

  • Domain or IP Address: IP address of the Scrutinizer collector that will download the logs

  • TCP Port: TCP port on the downloader to receive the logs

  • TLS Encryption: Can be enabled if desired, provided the downloader collector meets these requirements

  • App Connector Groups: Select the App Connector group(s) whose logs should be streamed to Scrutinizer

Log Stream tab#

  • Log Type: Select a supported log type (App Connector Status, Private Service Edge Status, Private Cloud Controller Status, Browser Access, or User Activity) to be streamed

  • Log Template: Select JSON

  • Log Stream Content: LogTimestamp is required, but other fields can be included/removed as needed (may affect default report types but custom reports can still be created). Additionally, all time fields (LogTimestamp, etc.) must be updated to the format %J{TIME_FIELD_NAME:epoch} (with a capital ‘J’ and the ‘:epoch’ suffix). See below for full stream content examples for each log type.

If more than one log type needs to be streamed to Scrutinizer, create a log receiver configuration for each type. Each configuration/type must use a unique TCP port.

Log stream content examples#

The following examples have all supported fields (including reformatted time fields) and can be pasted directly into the Log Stream Content field:

App Connector Status
{
    "LogTimestamp": %J{LogTimestamp:epoch},
    "Customer": %j{Customer},
    "SessionID": %j{SessionID},
    "SessionType": %j{SessionType},
    "SessionStatus": %j{SessionStatus},
    "Version": %j{Version},
    "Platform": %j{Platform},
    "ZEN": %j{ZEN},
    "Connector": %j{Connector},
    "ConnectorGroup": %j{ConnectorGroup},
    "PrivateIP": %j{PrivateIP},
    "PublicIP": %j{PublicIP},
    "Latitude": %f{Latitude},
    "Longitude": %f{Longitude},
    "CountryCode": %j{CountryCode},
    "TimestampAuthentication": %J{TimestampAuthentication:epoch},
    "TimestampUnAuthentication": %J{TimestampUnAuthentication:epoch},
    "CPUUtilization": %d{CPUUtilization},
    "MemUtilization": %d{MemUtilization},
    "ServiceCount": %d{ServiceCount},
    "InterfaceDefRoute": %j{InterfaceDefRoute},
    "DefRouteGW": %j{DefRouteGW},
    "PrimaryDNSResolver": %j{PrimaryDNSResolver},
    "HostStartTime": %J{HostStartTime:epoch},
    "ConnectorStartTime": %J{ConnectorStartTime:epoch},
    "NumOfInterfaces": %d{NumOfInterfaces},
    "BytesRxInterface": %d{BytesRxInterface},
    "PacketsRxInterface": %d{PacketsRxInterface},
    "ErrorsRxInterface": %d{ErrorsRxInterface},
    "DiscardsRxInterface": %d{DiscardsRxInterface},
    "BytesTxInterface": %d{BytesTxInterface},
    "PacketsTxInterface": %d{PacketsTxInterface},
    "ErrorsTxInterface": %d{ErrorsTxInterface},
    "DiscardsTxInterface": %d{DiscardsTxInterface},
    "TotalBytesRx": %d{TotalBytesRx},
    "TotalBytesTx": %d{TotalBytesTx},
    "MicroTenantID": %j{MicroTenantID}
}\n
Private Service Edge Status
{
    "LogTimestamp": %J{LogTimestamp:epoch},
    "Customer": %j{Customer},
    "SessionID": %j{SessionID},
    "SessionType": %j{SessionType},
    "SessionStatus": %j{SessionStatus},
    "Version": %j{Version},
    "PackageVersion": %j{PackageVersion},
    "Platform": %j{Platform},
    "ZEN": %j{ZEN},
    "ServiceEdge": %j{ServiceEdge},
    "ServiceEdgeGroup": %j{ServiceEdgeGroup},
    "PrivateIP": %j{PrivateIP},
    "PublicIP": %j{PublicIP},
    "Latitude": %f{Latitude},
    "Longitude": %f{Longitude},
    "CountryCode": %j{CountryCode},
    "TimestampAuthentication": %J{TimestampAuthentication:epoch},
    "TimestampUnAuthentication": %J{TimestampUnAuthentication:epoch},
    "CPUUtilization": %d{CPUUtilization},
    "MemUtilization": %d{MemUtilization},
    "InterfaceDefRoute": %j{InterfaceDefRoute},
    "DefRouteGW": %j{DefRouteGW},
    "PrimaryDNSResolver": %j{PrimaryDNSResolver},
    "HostUpTime": %J{HostUpTime:epoch},
    "ServiceEdgeStartTime": %J{ServiceEdgeStartTime:epoch},
    "NumOfInterfaces": %d{NumOfInterfaces},
    "BytesRxInterface": %d{BytesRxInterface},
    "PacketsRxInterface": %d{PacketsRxInterface},
    "ErrorsRxInterface": %d{ErrorsRxInterface},
    "DiscardsRxInterface": %d{DiscardsRxInterface},
    "BytesTxInterface": %d{BytesTxInterface},
    "PacketsTxInterface": %d{PacketsTxInterface},
    "ErrorsTxInterface": %d{ErrorsTxInterface},
    "DiscardsTxInterface": %d{DiscardsTxInterface},
    "TotalBytesRx": %d{TotalBytesRx},
    "TotalBytesTx": %d{TotalBytesTx},
    "MicroTenantID": %j{MicroTenantID}
}\n
Private Cloud Controller Status
{
    "LogTimestamp": %J{LogTimestamp:epoch},
    "Customer": %j{Customer},
    "SessionID": %j{SessionID},
    "SessionType": %j{SessionType},
    "SessionStatus": %j{SessionStatus},
    "Version": %j{Version},
    "PackageVersion": %j{PackageVersion},
    "Platform": %j{Platform},
    "ZEN": %j{ZEN},
    "PrivateCloudController": %j{PrivateCloudController},
    "PrivateCloudControllerGroup": %j{PrivateCloudControllerGroup},
    "PrivateIP": %j{PrivateIP},
    "PublicIP": %j{PublicIP},
    "Latitude": %f{Latitude},
    "Longitude": %f{Longitude},
    "CountryCode": %j{CountryCode},
    "TimestampAuthentication": %J{TimestampAuthentication:epoch},
    "TimestampUnAuthentication": %J{TimestampUnAuthentication:epoch},
    "CPUUtilization": %d{CPUUtilization},
    "MemUtilization": %d{MemUtilization},
    "InterfaceDefRoute": %j{InterfaceDefRoute},
    "DefRouteGW": %j{DefRouteGW},
    "PrimaryDNSResolver": %j{PrimaryDNSResolver},
    "HostUpTime": %J{HostUpTime:epoch},
    "PrivateCloudControllerStartTime": %J{PrivateCloudControllerStartTime:epoch},
    "NumOfInterfaces": %d{NumOfInterfaces},
    "BytesRxInterface": %d{BytesRxInterface},
    "PacketsRxInterface": %d{PacketsRxInterface},
    "ErrorsRxInterface": %d{ErrorsRxInterface},
    "DiscardsRxInterface": %d{DiscardsRxInterface},
    "BytesTxInterface": %d{BytesTxInterface},
    "PacketsTxInterface": %d{PacketsTxInterface},
    "ErrorsTxInterface": %d{ErrorsTxInterface},
    "DiscardsTxInterface": %d{DiscardsTxInterface},
    "TotalBytesRx": %d{TotalBytesRx},
    "TotalBytesTx": %d{TotalBytesTx},
    "MicroTenantID": %j{MicroTenantID}
}\n
Browser Access
{
    "LogTimestamp":%J{LogTimestamp:epoch},
    "ConnectionID":%j{ConnectionID},
    "Exporter":%j{Exporter},
    "TimestampRequestReceiveStart":%J{TimestampRequestReceiveStart:epoch},
    "TimestampRequestReceiveHeaderFinish":%J{TimestampRequestReceiveHeaderFinish:epoch},
    "TimestampRequestReceiveFinish":%J{TimestampRequestReceiveFinish:epoch},
    "TimestampRequestTransmitStart":%J{TimestampRequestTransmitStart:epoch},
    "TimestampRequestTransmitFinish":%J{TimestampRequestTransmitFinish:epoch},
    "TimestampResponseReceiveStart":%J{TimestampResponseReceiveStart:epoch},
    "TimestampResponseReceiveFinish":%J{TimestampResponseReceiveFinish:epoch},
    "TimestampResponseTransmitStart":%J{TimestampResponseTransmitStart:epoch},
    "TimestampResponseTransmitFinish":%J{TimestampResponseTransmitFinish:epoch},
    "TotalTimeRequestReceive":%d{TotalTimeRequestReceive},
    "TotalTimeRequestTransmit":%d{TotalTimeRequestTransmit},
    "TotalTimeResponseReceive":%d{TotalTimeResponseReceive},
    "TotalTimeResponseTransmit":%d{TotalTimeResponseTransmit},
    "TotalTimeConnectionSetup":%d{TotalTimeConnectionSetup},
    "TotalTimeServerResponse":%d{TotalTimeServerResponse},
    "Method":%j{Method},"Protocol":%j{Protocol},
    "Host":%j{Host},
    "URL":%j{URL},
    "UserAgent":%j{UserAgent},
    "XFF":%j{XFF},
    "NameID":%j{NameID},
    "StatusCode":%d{StatusCode},
    "RequestSize":%d{RequestSize},
    "ResponseSize":%d{ResponseSize},
    "ApplicationPort":%d{ApplicationPort},
    "ClientPublicIp":%j{ClientPublicIp},
    "ClientPublicPort":%d{ClientPublicPort},
    "ClientPrivateIp":%j{ClientPrivateIp},
    "Customer":%j{Customer},
    "ConnectionStatus":%j{ConnectionStatus},
    "ConnectionReason":%j{ConnectionReason},
    "Origin":%j{Origin},
    "CorsToken":%j{CorsToken}
}\n
User Activity
{
    "LogTimestamp": %J{LogTimestamp:epoch},
    "Customer": %j{Customer},
    "SessionID": %j{SessionID},
    "ConnectionID": %j{ConnectionID},
    "InternalReason": %j{InternalReason},
    "ConnectionStatus": %j{ConnectionStatus},
    "IPProtocol": %d{IPProtocol},
    "DoubleEncryption": %d{DoubleEncryption},
    "Username": %j{Username},
    "ServicePort": %d{ServicePort},
    "ClientPublicIP": %j{ClientPublicIP},
    "ClientPrivateIP": %j{ClientPrivateIP},
    "ClientLatitude": %f{ClientLatitude},
    "ClientLongitude": %f{ClientLongitude},
    "ClientCountryCode": %j{ClientCountryCode},
    "ClientZEN": %j{ClientZEN},
    "Policy": %j{Policy},
    "Connector": %j{Connector},
    "ConnectorZEN": %j{ConnectorZEN},
    "ConnectorIP": %j{ConnectorIP},
    "ConnectorPort": %d{ConnectorPort},
    "Host": %j{Host},
    "Application": %j{Application},
    "AppGroup": %j{AppGroup},
    "Server": %j{Server},
    "ServerIP": %j{ServerIP},
    "ServerPort": %d{ServerPort},
    "PolicyProcessingTime": %d{PolicyProcessingTime},
    "ServerSetupTime": %d{ServerSetupTime},
    "TimestampConnectionStart": %J{TimestampConnectionStart:epoch},
    "TimestampConnectionEnd": %J{TimestampConnectionEnd:epoch},
    "TimestampCATx": %J{TimestampCATx:epoch},
    "TimestampCARx": %J{TimestampCARx:epoch},
    "TimestampAppLearnStart": %J{TimestampAppLearnStart:epoch},
    "TimestampZENFirstRxClient": %J{TimestampZENFirstRxClient:epoch},
    "TimestampZENFirstTxClient": %J{TimestampZENFirstTxClient:epoch},
    "TimestampZENLastRxClient": %J{TimestampZENLastRxClient:epoch},
    "TimestampZENLastTxClient": %J{TimestampZENLastTxClient:epoch},
    "TimestampConnectorZENSetupComplete": %J{TimestampConnectorZENSetupComplete:epoch},
    "TimestampZENFirstRxConnector": %J{TimestampZENFirstRxConnector:epoch},
    "TimestampZENFirstTxConnector": %J{TimestampZENFirstTxConnector:epoch},
    "TimestampZENLastRxConnector": %J{TimestampZENLastRxConnector:epoch},
    "TimestampZENLastTxConnector": %J{TimestampZENLastTxConnector:epoch},
    "ZENTotalBytesRxClient": %d{ZENTotalBytesRxClient},
    "ZENBytesRxClient": %d{ZENBytesRxClient},
    "ZENTotalBytesTxClient": %d{ZENTotalBytesTxClient},
    "ZENBytesTxClient": %d{ZENBytesTxClient},
    "ZENTotalBytesRxConnector": %d{ZENTotalBytesRxConnector},
    "ZENBytesRxConnector": %d{ZENBytesRxConnector},
    "ZENTotalBytesTxConnector": %d{ZENTotalBytesTxConnector},
    "ZENBytesTxConnector": %d{ZENBytesTxConnector},
    "Idp": %j{Idp},"ClientToClient": %j{c2c},
    "ClientCity": %j{ClientCity},
    "MicroTenantID": %j{MicroTenantID},
    "AppMicroTenantID": %j{AppMicroTenantID},
    "Platform": %j{Platform},
    "Hostname": %j{Hostname}
}\n

Configuring Zscaler ZPA log ingestion in Scrutinizer#

After adding the log receiver configuration, configure Scrutinizer to receive the logs:

  1. In the Scrutinizer web interface, navigate to Admin > Integrations > Flow Log Ingestion.

  2. Click the + icon, then select Zscaler ZPA in the tray.

  3. In the secondary tray, configure the following details:

    • Log Source: Select the log type that was set to be streamed

    • Log Downloader: IP address of the Scrutinizer server/collector that will download the logs (must match the address entered for the log receiver configuration)

    • Log Downloader Port: Port to use to receive logs on the downloader (must match the port entered for the log receiver configuration)

    • Worker Count: Enter 1 (can be increased if log streaming rate seems slow; recommended maximum of 10)

    • Flow Collectors: Select the Scrutinizer collector(s) that will receive the logs as standard flows

  4. Click Save to add the log stream with the current settings.

Once added, the log stream will be listed in the main Admin > Integrations > Flow Log Ingestion view under the downloader IP address and port configured. If multiple log receivers were created to stream different ZPA log types, each one will require a matching configuration in Scrutinizer.

Note

  • To verify that the log downloader was successfully configured, look for the following message (or something similar) in /home/plixer/scrutinizer/files/logs/zscaler_log.json on that server/collector and confirm that the bind value matches the configured TCP port:

    {"level":"warn","pid":2059650,"bind":":10000","time":"2025-06-16T14:34:50.916-04:00","caller":"/builds/plixer-products/scrutinizer/application/golang/zscaler/sink-tcp-server.go:131","message":"listening for connections"}
    
  • ZPA log data collection can be tested by running a new report and checking if the Zscaler ZPA report type category is available. ZPA log exporters will be listed using the IP address of the App Connector forwarding logs to Scrutinizer.

Troubleshooting#

MFSNs and a buildup of log files in flow log source containers are indications that the rate of flow and/or log generation exceeds the capacity of the collector assigned to the flow log source.

The following are potential solutions for an overloaded collector:

  • If the collector is a VM, allocate additional resources (starting with CPU cores) to it.

  • If the collector is ingesting flow logs from only one source (bucket or container), distribute the logs across multiple sources, which can then be assigned to different collectors.

  • If the collector is ingesting flow logs from multiple sources, reassign sources across multiple collectors.

  • If the collector license has a flow rate limit, the license may need to be upgraded.

Note

  • In distributed deployments, it is recommended to start with a 1:1 pairing of sources and collectors.

  • The Unresourced - Enabled status in the Admin > Resources > Exporters view is another indication that flow log sources are being temporarily disabled/paused due to insufficient resources.

If the Admin > Resources > Exporters view does not list exporters that are associated with the virtual network(s) set up for flow ingestion, do the following:

  1. Navigate to Admin > Integrations > Flow Ingestion, open the configuration tray for the collector it was assigned to, and then use the Test button to verify that the correct details were entered.

    Note

    The Test button only checks if the communication with the data source works.

  2. Verify that flow logs are correctly being sent to the bucket or container.

  3. Check the collector log file in /home/plixer/scrutinizer/files/logs/ for errors.

  4. Check awss3_log.json (AWS), azure_log.json (Azure), or ocist_log.json for possible source-side issues.

Note

The Admin > Resources > Exporters view also displays exporters that have been disabled. Because each AWS, Azure, or OCI flow log source counts as an exporter, one or more sources may be disabled automatically (in last-in/first-out order) if the exporter count limit of the current license is reached.